diff --git a/.babelrc b/.babelrc index a5e10d850..398679c94 100644 --- a/.babelrc +++ b/.babelrc @@ -15,6 +15,12 @@ "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-transform-react-jsx", - "@babel/plugin-transform-runtime" + "@babel/plugin-transform-runtime", + [ + "@babel/proposal-decorators", + { + "decoratorsBeforeExport": true + } + ] ] } diff --git a/lerna.json b/lerna.json index dbb764929..816197ce1 100644 --- a/lerna.json +++ b/lerna.json @@ -17,7 +17,8 @@ "packages/extensions/debug/main", "packages/extensions/luis", "packages/extensions/luis/client", - "packages/extensions/json" + "packages/extensions/json", + "packages/extensions/botState" ], "version": "independent" } diff --git a/package-lock.json b/package-lock.json index 6d6f20213..c9c0f0025 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,19 @@ "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-4.0.2.tgz", "integrity": "sha512-XtGk+IF57pr852UK1AhQJXqmm1WmSgS5uISL+LPs0z/iAxXouMvdlLJrHPeukP6gd7yR2rDTMSMkHNODgwIq7A==" }, + "@azure/ms-rest-js": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.2.6.tgz", + "integrity": "sha512-8cmDpxsQjVdveJwYKtNnkJorxEORLYJu9UHaUvLZA6yHExzDeISHAcSVWE0J05+VkJtqheVHF17M+2ro18Cdnw==", + "requires": { + "axios": "^0.18.0", + "form-data": "^2.3.2", + "tough-cookie": "^2.4.3", + "tslib": "^1.9.2", + "uuid": "^3.2.1", + "xml2js": "^0.4.19" + } + }, "@babel/cli": { "version": "7.4.3", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.4.3.tgz", @@ -354,6 +367,17 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-proposal-decorators": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.4.0.tgz", + "integrity": "sha512-d08TLmXeK/XbgCo7ZeZ+JaeZDtDai/2ctapTRsWWkkmy7G/cqz8DQN/HlWG7RR4YmfXxmExsbU3SuCjlM7AtUg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.4.0", + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-syntax-decorators": "^7.2.0" + } + }, "@babel/plugin-proposal-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.2.0.tgz", @@ -441,6 +465,15 @@ "@babel/helper-plugin-utils": "^7.0.0" } }, + "@babel/plugin-syntax-decorators": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.2.0.tgz", + "integrity": "sha512-38QdqVoXdHUQfTpZo3rQwqQdWtCn5tMv4uV6r2RMfTqNBuv4ZBhz79SfaQWKTVmxHjeFv/DnXVC/+agHCklYWA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, "@babel/plugin-syntax-json-strings": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.2.0.tgz", @@ -2211,6 +2244,239 @@ "@types/node": "*" } }, + "@types/d3": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-5.7.2.tgz", + "integrity": "sha512-7/wClB8ycneWGy3jdvLfXKTd5SoTg9hji7IdJ0RuO9xTY54YpJ8zlcFADcXhY1J3kCBwxp+/1jeN6a5OMwgYOw==", + "requires": { + "@types/d3-array": "^1", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-collection": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-voronoi": "*", + "@types/d3-zoom": "*" + } + }, + "@types/d3-array": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.7.tgz", + "integrity": "sha512-51vHWuUyDOi+8XuwPrTw3cFqyh2Slg9y8COYkRfjCPG9TfYqY0hoNPzv/8BrcAy0FeQBzqEo/D/8Nk2caOQJnA==" + }, + "@types/d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-BZISgSD5M8TgURyNtcPAmUB9sk490CO1Thb6/gIn0WZTt3Y50IssX+2Z0vTccoqZksUDTep0b+o4ofXslvNbqg==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-brush": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-1.0.10.tgz", + "integrity": "sha512-J8jREATIrfJaAfhJivqaEKPnJsRlwwrOPje+ABqZFgamADjll+q9zaDXnYyjiGPPsiJEU+Qq9jQi5rECxIOfhg==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-chord": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-1.0.9.tgz", + "integrity": "sha512-UA6lI9CVW5cT5Ku/RV4hxoFn4mKySHm7HEgodtfRthAj1lt9rKZEPon58vyYfk+HIAm33DtJJgZwMXy2QgyPXw==" + }, + "@types/d3-collection": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-collection/-/d3-collection-1.0.8.tgz", + "integrity": "sha512-y5lGlazdc0HNO0F3UUX2DPE7OmYvd9Kcym4hXwrJcNUkDaypR5pX+apuMikl9LfTxKItJsY9KYvzBulpCKyvuQ==" + }, + "@types/d3-color": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.2.2.tgz", + "integrity": "sha512-6pBxzJ8ZP3dYEQ4YjQ+NVbQaOflfgXq/JbDiS99oLobM2o72uAST4q6yPxHv6FOTCRC/n35ktuo8pvw/S4M7sw==" + }, + "@types/d3-contour": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-1.3.0.tgz", + "integrity": "sha512-AUCUIjEnC5lCGBM9hS+MryRaFLIrPls4Rbv6ktqbd+TK/RXZPwOy9rtBWmGpbeXcSOYCJTUDwNJuEnmYPJRxHQ==", + "requires": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "@types/d3-dispatch": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-1.0.7.tgz", + "integrity": "sha512-M+z84G7UKwK6hEPnGCSccOg8zJ3Nk2hgDQ9sCstHXgsFU0sMxlIZVKqKB5oxUDbALqQG6ucg0G9e8cmOSlishg==" + }, + "@types/d3-drag": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-1.2.3.tgz", + "integrity": "sha512-rWB5SPvkYVxW3sqUxHOJUZwifD0KqvKwvt1bhNqcLpW6Azsd0BJgRNcyVW8GAferaAk5r8dzeZnf9zKlg9+xMQ==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-dsv": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-1.0.36.tgz", + "integrity": "sha512-jbIWQ27QJcBNMZbQv0NSQMHnBDCmxghAxePxgyiPH1XPCRkOsTBei7jcdi3fDrUCGpCV3lKrSZFSlOkhUQVClA==" + }, + "@types/d3-ease": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-1.0.8.tgz", + "integrity": "sha512-VRf8czVWHSJPoUWxMunzpePK02//wHDAswknU8QWzcyrQn6pqe46bHRYi2smSpw5VjsT2CG8k/QeWIdWPS3Bmg==" + }, + "@types/d3-fetch": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-1.1.5.tgz", + "integrity": "sha512-o9c0ItT5/Gl3wbNuVpzRnYX1t3RghzeWAjHUVLuyZJudiTxC4f/fC0ZPFWLQ2lVY8pAMmxpV8TJ6ETYCgPeI3A==", + "requires": { + "@types/d3-dsv": "*" + } + }, + "@types/d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-jqK+I36uz4kTBjyk39meed5y31Ab+tXYN/x1dn3nZEus9yOHCLc+VrcIYLc/aSQ0Y7tMPRlIhLetulME76EiiA==" + }, + "@types/d3-format": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-1.3.1.tgz", + "integrity": "sha512-KAWvReOKMDreaAwOjdfQMm0HjcUMlQG47GwqdVKgmm20vTd2pucj0a70c3gUSHrnsmo6H2AMrkBsZU2UhJLq8A==" + }, + "@types/d3-geo": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-1.11.1.tgz", + "integrity": "sha512-Ox8WWOG3igDRoep/dNsGbOiSJYdUG3ew/6z0ETvHyAtXZVBjOE0S96zSSmzgl0gqQ3RdZjn2eeJOj9oRcMZPkQ==", + "requires": { + "@types/geojson": "*" + } + }, + "@types/d3-hierarchy": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz", + "integrity": "sha512-vvSaIDf/Ov0o3KwMT+1M8+WbnnlRiGjlGD5uvk83a1mPCTd/E5x12bUJ/oP55+wUY/4Kb5kc67rVpVGJ2KUHxg==" + }, + "@types/d3-interpolate": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-1.3.1.tgz", + "integrity": "sha512-z8Zmi08XVwe8e62vP6wcA+CNuRhpuUU5XPEfqpG0hRypDE5BWNthQHB1UNWWDB7ojCbGaN4qBdsWp5kWxhT1IQ==", + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.8.tgz", + "integrity": "sha512-AZGHWslq/oApTAHu9+yH/Bnk63y9oFOMROtqPAtxl5uB6qm1x2lueWdVEjsjjV3Qc2+QfuzKIwIR5MvVBakfzA==" + }, + "@types/d3-polygon": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-1.0.7.tgz", + "integrity": "sha512-Xuw0eSjQQKs8jTiNbntWH0S+Xp+JyhqxmQ0YAQ3rDu6c3kKMFfgsaGN7Jv5u3zG6yVX/AsLP/Xs/QRjmi9g43Q==" + }, + "@types/d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-0ajFawWicfjsaCLh6NzxOyVDYhQAmMFbsiI3MPGLInorauHFEh9/Cl6UHNf+kt/J1jfoxKY/ZJaKAoDpbvde5Q==" + }, + "@types/d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-Jui+Zn28pQw/3EayPKaN4c/PqTvqNbIPjHkgIIFnxne1FdwNjfHtAIsZIBMKlquQNrrMjFzCrlF2gPs3xckqaA==" + }, + "@types/d3-scale": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-2.1.1.tgz", + "integrity": "sha512-kNTkbZQ+N/Ip8oX9PByXfDLoCSaZYm+VUOasbmsa6KD850/ziMdYepg/8kLg2plHzoLANdMqPoYQbvExevLUHg==", + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-1.3.1.tgz", + "integrity": "sha512-Ny3rLbV5tnmqgW7w/poCcef4kXP8mHPo/p8EjTS5d9OUk8MlqAeRaM8eF7Vyv7QMLiIXNE94Pa1cMLSPkXQBoQ==" + }, + "@types/d3-selection": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.4.1.tgz", + "integrity": "sha512-bv8IfFYo/xG6dxri9OwDnK3yCagYPeRIjTlrcdYJSx+FDWlCeBDepIHUpqROmhPtZ53jyna0aUajZRk0I3rXNA==" + }, + "@types/d3-shape": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.1.tgz", + "integrity": "sha512-usqdvUvPJ7AJNwpd2drOzRKs1ELie53p2m2GnPKr076/ADM579jVTJ5dPsoZ5E/CMNWk8lvPWYQSvilpp6jjwg==", + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.0.10.tgz", + "integrity": "sha512-aKf62rRQafDQmSiv1NylKhIMmznsjRN+MnXRXTqHoqm0U/UZzVpdrtRnSIfdiLS616OuC1soYeX1dBg2n1u8Xw==" + }, + "@types/d3-time-format": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.1.tgz", + "integrity": "sha512-tJSyXta8ZyJ52wDDHA96JEsvkbL6jl7wowGmuf45+fAkj5Y+SQOnz0N7/H68OWmPshPsAaWMQh+GAws44IzH3g==" + }, + "@types/d3-timer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-WvfJ3LFxBbWjqRGz9n7GJt08RrTHPJDVsIwwoCMROlqF+iDacYiAFjf9oqnq0mXpb2juA2N/qjKP+MKdal3YNQ==" + }, + "@types/d3-transition": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-1.1.4.tgz", + "integrity": "sha512-/vsmKVUIXEyCcIXYAlw7bnYkIs9/J/nZbptRJFKUN3FdXq/dF6j9z9xXzerkyU6TDHLrMrwx9eGwdKyTIy/j9w==", + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-voronoi": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@types/d3-voronoi/-/d3-voronoi-1.1.9.tgz", + "integrity": "sha512-DExNQkaHd1F3dFPvGA/Aw2NGyjMln6E9QzsiqOcBgnE+VInYnFBHBBySbZQts6z6xD+5jTfKCP7M4OqMyVjdwQ==" + }, + "@types/d3-zoom": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-1.7.4.tgz", + "integrity": "sha512-5jnFo/itYhJeB2khO/lKe730kW/h2EbKMOvY0uNp3+7NdPm4w63DwPEMxifQZ7n902xGYK5DdU67FmToSoy4VA==", + "requires": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "@types/deep-diff": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/deep-diff/-/deep-diff-1.0.0.tgz", + "integrity": "sha512-ENsJcujGbCU/oXhDfQ12mSo/mCBWodT2tpARZKmatoSrf8+cGRCPi0KVj3I0FORhYZfLXkewXu7AoIWqiBLkNw==" + }, "@types/enzyme": { "version": "3.9.1", "resolved": "https://registry.npmjs.org/@types/enzyme/-/enzyme-3.9.1.tgz", @@ -2269,6 +2535,11 @@ "@types/node": "*" } }, + "@types/geojson": { + "version": "7946.0.7", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.7.tgz", + "integrity": "sha512-wE2v81i4C4Ol09RtsWFAqg3BUitWbHSpSlIo+bNdsCJijO9sjme+zm+73ZMCa/qMC8UEERxzGbvmr1cffo2SiQ==" + }, "@types/jest": { "version": "22.2.3", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-22.2.3.tgz", @@ -3066,7 +3337,7 @@ }, "array-ify": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/array-ify/-/array-ify-1.0.0.tgz", "integrity": "sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=" }, "array-includes": { @@ -3175,6 +3446,11 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==" + }, "assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -3329,6 +3605,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.0.tgz", + "integrity": "sha1-MtU+SFHv3AoRmTts0AB4nXDAUQI=", + "requires": { + "follow-redirects": "^1.3.0", + "is-buffer": "^1.1.5" + } + }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -4518,6 +4803,52 @@ } } }, + "botframework-connector": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/botframework-connector/-/botframework-connector-4.3.4.tgz", + "integrity": "sha512-aaUHVcgX1m1uwBNxdD5UrNo1f5t7zHDM/h6ha8wJMBqzwc7KXklTwagZccsw18NA1v5g5hVB8S2WgVXCeumXLg==", + "requires": { + "@azure/ms-rest-js": "1.2.6", + "@types/jsonwebtoken": "7.2.8", + "@types/node": "^10.12.18", + "base64url": "^3.0.0", + "botframework-schema": "^4.3.4", + "form-data": "^2.3.3", + "jsonwebtoken": "8.0.1", + "nock": "^10.0.3", + "node-fetch": "^2.2.1", + "rsa-pem-from-mod-exp": "^0.8.4" + }, + "dependencies": { + "@types/node": { + "version": "10.14.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz", + "integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg==" + }, + "base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==" + }, + "jsonwebtoken": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.0.1.tgz", + "integrity": "sha1-UNrvjQqMfeLNBrwQE7dbBMzz8M8=", + "requires": { + "jws": "^3.1.4", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.0.0", + "xtend": "^4.0.1" + } + } + } + }, "botframework-directlinejs": { "version": "0.11.4", "resolved": "https://registry.npmjs.org/botframework-directlinejs/-/botframework-directlinejs-0.11.4.tgz", @@ -4950,7 +5281,7 @@ }, "byline": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/byline/-/byline-5.0.0.tgz", "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE=" }, "byte-size": { @@ -5135,6 +5466,19 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chai": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", + "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.0", + "type-detect": "^4.0.5" + } + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -5199,6 +5543,11 @@ } } }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + }, "cheerio": { "version": "1.0.0-rc.3", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz", @@ -5646,7 +5995,7 @@ }, "cmd-shim": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-2.0.2.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/cmd-shim/-/cmd-shim-2.0.2.tgz", "integrity": "sha1-b8vamUg6j9FdfTChlspp1oii79s=", "requires": { "graceful-fs": "^4.1.2", @@ -5739,7 +6088,7 @@ }, "columnify": { "version": "1.5.4", - "resolved": "https://registry.npmjs.org/columnify/-/columnify-1.5.4.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/columnify/-/columnify-1.5.4.tgz", "integrity": "sha1-Rzfd8ce2mop8NAVweC6UfuyOeLs=", "requires": { "strip-ansi": "^3.0.0", @@ -5782,7 +6131,7 @@ }, "compare-func": { "version": "1.3.2", - "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-1.3.2.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/compare-func/-/compare-func-1.3.2.tgz", "integrity": "sha1-md0LpFfh+bxyKxLAjsM+6rMfpkg=", "requires": { "array-ify": "^1.0.0", @@ -5791,7 +6140,7 @@ "dependencies": { "dot-prop": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-3.0.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/dot-prop/-/dot-prop-3.0.0.tgz", "integrity": "sha1-G3CK8JSknJoOfbyteQq6U52sEXc=", "requires": { "is-obj": "^1.0.0" @@ -7126,6 +7475,270 @@ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=" }, + "d3": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.9.2.tgz", + "integrity": "sha512-ydrPot6Lm3nTWH+gJ/Cxf3FcwuvesYQ5uk+j/kXEH/xbuYWYWTMAHTJQkyeuG8Y5WM5RSEYB41EctUrXQQytRQ==", + "requires": { + "d3-array": "1", + "d3-axis": "1", + "d3-brush": "1", + "d3-chord": "1", + "d3-collection": "1", + "d3-color": "1", + "d3-contour": "1", + "d3-dispatch": "1", + "d3-drag": "1", + "d3-dsv": "1", + "d3-ease": "1", + "d3-fetch": "1", + "d3-force": "1", + "d3-format": "1", + "d3-geo": "1", + "d3-hierarchy": "1", + "d3-interpolate": "1", + "d3-path": "1", + "d3-polygon": "1", + "d3-quadtree": "1", + "d3-random": "1", + "d3-scale": "2", + "d3-scale-chromatic": "1", + "d3-selection": "1", + "d3-shape": "1", + "d3-time": "1", + "d3-time-format": "2", + "d3-timer": "1", + "d3-transition": "1", + "d3-voronoi": "1", + "d3-zoom": "1" + } + }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + }, + "d3-brush": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz", + "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "requires": { + "d3-array": "1", + "d3-path": "1" + } + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz", + "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==" + }, + "d3-contour": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", + "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "requires": { + "d3-array": "^1.1.1" + } + }, + "d3-dispatch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", + "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" + }, + "d3-drag": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz", + "integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-dsv": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.1.1.tgz", + "integrity": "sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==", + "requires": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + } + }, + "d3-ease": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz", + "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==" + }, + "d3-fetch": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.1.2.tgz", + "integrity": "sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==", + "requires": { + "d3-dsv": "1" + } + }, + "d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", + "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" + }, + "d3-geo": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.11.3.tgz", + "integrity": "sha512-n30yN9qSKREvV2fxcrhmHUdXP9TNH7ZZj3C/qnaoU0cVf/Ea85+yT7HY7i8ySPwkwjCNYtmKqQFTvLFngfkItQ==", + "requires": { + "d3-array": "1" + } + }, + "d3-hierarchy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", + "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==" + }, + "d3-interpolate": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", + "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", + "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==" + }, + "d3-polygon": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.5.tgz", + "integrity": "sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w==" + }, + "d3-quadtree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.6.tgz", + "integrity": "sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==" + }, + "d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + }, + "d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "d3-scale-chromatic": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz", + "integrity": "sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==", + "requires": { + "d3-color": "1", + "d3-interpolate": "1" + } + }, + "d3-selection": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.0.tgz", + "integrity": "sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==" + }, + "d3-shape": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.5.tgz", + "integrity": "sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz", + "integrity": "sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==" + }, + "d3-time-format": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", + "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" + }, + "d3-transition": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.2.0.tgz", + "integrity": "sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, + "d3-zoom": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.7.3.tgz", + "integrity": "sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, "dargs": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/dargs/-/dargs-4.1.0.tgz", @@ -7204,7 +7817,7 @@ }, "decamelize-keys": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/decamelize-keys/-/decamelize-keys-1.1.0.tgz", "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", "requires": { "decamelize": "^1.1.0", @@ -7226,9 +7839,22 @@ }, "dedent": { "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/dedent/-/dedent-0.7.0.tgz", "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=" }, + "deep-diff": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/deep-diff/-/deep-diff-1.0.2.tgz", + "integrity": "sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg==" + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "requires": { + "type-detect": "^4.0.0" + } + }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -7330,7 +7956,7 @@ }, "defaults": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/defaults/-/defaults-1.0.3.tgz", "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", "requires": { "clone": "^1.0.2" @@ -7372,7 +7998,7 @@ "dependencies": { "globby": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "resolved": "http://registry.npmjs.org/globby/-/globby-6.1.0.tgz", "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "requires": { "array-union": "^1.0.1", @@ -7384,7 +8010,7 @@ "dependencies": { "pify": { "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" } } @@ -10234,6 +10860,11 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=" + }, "get-own-enumerable-property-symbols": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz", @@ -10242,7 +10873,7 @@ }, "get-pkg-repo": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/get-pkg-repo/-/get-pkg-repo-1.4.0.tgz", "integrity": "sha1-xztInAbYDMVTbCyFP54FIyBWly0=", "requires": { "hosted-git-info": "^2.1.4", @@ -10403,7 +11034,7 @@ }, "git-remote-origin-url": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", "integrity": "sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=", "requires": { "gitconfiglocal": "^1.0.0", @@ -10554,7 +11185,7 @@ }, "gitconfiglocal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", "integrity": "sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=", "requires": { "ini": "^1.3.2" @@ -11956,7 +12587,7 @@ }, "is-text-path": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/is-text-path/-/is-text-path-1.0.1.tgz", "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "requires": { "text-extensions": "^1.0.0" @@ -13590,7 +14221,7 @@ }, "jsonparse": { "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, "jsonpointer": { @@ -15327,6 +15958,32 @@ "lower-case": "^1.1.1" } }, + "nock": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-10.0.6.tgz", + "integrity": "sha512-b47OWj1qf/LqSQYnmokNWM8D88KvUl2y7jT0567NB3ZBAZFz2bWp2PC81Xn7u8F2/vJxzkzNZybnemeFa7AZ2w==", + "requires": { + "chai": "^4.1.2", + "debug": "^4.1.0", + "deep-equal": "^1.0.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.5", + "mkdirp": "^0.5.0", + "propagate": "^1.0.0", + "qs": "^6.5.1", + "semver": "^5.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, "node-dev": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/node-dev/-/node-dev-3.1.3.tgz", @@ -16634,7 +17291,7 @@ }, "parse-github-repo-url": { "version": "1.4.1", - "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=" }, "parse-glob": { @@ -16801,6 +17458,11 @@ } } }, + "pathval": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", + "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=" + }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", @@ -19497,6 +20159,11 @@ "reflect.ownkeys": "^0.2.0" } }, + "propagate": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-1.0.0.tgz", + "integrity": "sha1-AMLa7t2iDofjeCs0Stuhzd1q1wk=" + }, "property-expr": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-1.5.1.tgz", @@ -19671,7 +20338,7 @@ }, "quick-lru": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/quick-lru/-/quick-lru-1.1.0.tgz", "integrity": "sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=" }, "raf": { @@ -20104,7 +20771,7 @@ }, "read-cmd-shim": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz", "integrity": "sha1-LV0Vd4ajfAVdIgd8MsU/gynpHHs=", "requires": { "graceful-fs": "^4.1.2" @@ -20201,7 +20868,7 @@ }, "load-json-file": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "requires": { "graceful-fs": "^4.1.2", @@ -20914,6 +21581,11 @@ "aproba": "^1.1.1" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, "rx": { "version": "2.3.24", "resolved": "https://registry.npmjs.org/rx/-/rx-2.3.24.tgz", @@ -22442,7 +23114,7 @@ }, "temp-dir": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-1.0.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/temp-dir/-/temp-dir-1.0.0.tgz", "integrity": "sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=" }, "temp-file": { @@ -22478,7 +23150,7 @@ }, "temp-write": { "version": "3.4.0", - "resolved": "https://registry.npmjs.org/temp-write/-/temp-write-3.4.0.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/temp-write/-/temp-write-3.4.0.tgz", "integrity": "sha1-jP9jD7fp2gXwR8dM5M5NaFRX1JI=", "requires": { "graceful-fs": "^4.1.2", @@ -22819,7 +23491,7 @@ }, "trim-off-newlines": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz", "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=" }, "trim-right": { @@ -22886,6 +23558,11 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + }, "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", @@ -23734,7 +24411,7 @@ }, "wcwidth": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "resolved": "https://botbuilder.myget.org/F/botbuilder-v4-js-daily/npm/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", "requires": { "defaults": "^1.0.3" @@ -24291,6 +24968,15 @@ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, "xmlbuilder": { "version": "9.0.7", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", diff --git a/package.json b/package.json index a341c5d6f..6bdfc51f5 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@babel/plugin-transform-react-jsx": "^7.0.0", "@babel/preset-env": "^7.1.0", "@babel/preset-typescript": "^7.1.0", + "@babel/plugin-proposal-decorators": "^7.4.0", "babel-core": "^7.0.0-bridge.0", "babel-jest": "23.6.0", "husky": "^1.3.1", diff --git a/packages/app/client/package-lock.json b/packages/app/client/package-lock.json index bdaa928f2..e985f8204 100644 --- a/packages/app/client/package-lock.json +++ b/packages/app/client/package-lock.json @@ -4,720 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "copy-webpack-plugin": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz", - "integrity": "sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA==", - "dev": true, - "requires": { - "cacache": "^10.0.4", - "find-cache-dir": "^1.0.0", - "globby": "^7.1.1", - "is-glob": "^4.0.0", - "loader-utils": "^1.1.0", - "minimatch": "^3.0.4", - "p-limit": "^1.0.0", - "serialize-javascript": "^1.4.0" - }, - "dependencies": { - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "^1.0.1" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "cacache": { - "version": "10.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz", - "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", - "dev": true, - "requires": { - "bluebird": "^3.5.1", - "chownr": "^1.0.1", - "glob": "^7.1.2", - "graceful-fs": "^4.1.11", - "lru-cache": "^4.1.1", - "mississippi": "^2.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.2", - "ssri": "^5.2.4", - "unique-filename": "^1.1.0", - "y18n": "^4.0.0" - } - }, - "chownr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", - "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cyclist": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz", - "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=", - "dev": true - }, - "dir-glob": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", - "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", - "dev": true, - "requires": { - "path-type": "^3.0.0" - } - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "dev": true, - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "end-of-stream": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", - "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "find-cache-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-1.0.0.tgz", - "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^1.0.0", - "pkg-dir": "^2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "dir-glob": "^2.0.0", - "glob": "^7.1.2", - "ignore": "^3.3.5", - "pify": "^3.0.0", - "slash": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "ignore": { - "version": "3.3.10", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", - "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "loader-utils": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", - "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^2.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mississippi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz", - "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^2.0.1", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parallel-transform": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz", - "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", - "dev": true, - "requires": { - "cyclist": "~0.2.2", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "serialize-javascript": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.6.1.tgz", - "integrity": "sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw==", - "dev": true - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "ssri": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz", - "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.1" - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-shift": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", - "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz", - "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, "eslint-plugin-react": { "version": "7.12.3", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.12.3.tgz", diff --git a/packages/app/client/package.json b/packages/app/client/package.json index c9ccbec01..aabfaf1d3 100644 --- a/packages/app/client/package.json +++ b/packages/app/client/package.json @@ -28,6 +28,9 @@ "testMatch": [ "**/?(*.)(spec|test).(ts)?(x)" ], + "moduleNameMapper": { + ".\\.scss$": "/../../../../jestMocks/styleMock.js" + }, "moduleFileExtensions": [ "ts", "tsx", @@ -47,6 +50,7 @@ "@babel/preset-env": "^7.1.0", "@babel/preset-typescript": "^7.1.0", "@types/enzyme": "^3.1.10", + "@types/deep-diff": "^1.0.0", "@types/jest": "^22.2.3", "@types/react": "~16.3.2", "@types/react-dom": "^16.0.4", @@ -102,8 +106,11 @@ "@uifabric/styling": "^5.20.0", "base64url": "2.0.0", "botframework-config": "4.0.0-preview1.3.4", + "botframework-schema": "^4.3.4", "botframework-webchat": "^4.3.0", - "eslint-plugin-react": "7.12.3", + "botframework-webchat-core": "^4.3.0", + "deep-diff": "^1.0.2", + "eslint-plugin-react": "^7.12.3", "react": "~16.3.2", "react-dom": "~16.3.2", "react-redux": "^5.0.7", diff --git a/packages/app/client/src/commands/emulatorCommands.spec.ts b/packages/app/client/src/commands/emulatorCommands.spec.ts index ab2c8d109..339c50cab 100644 --- a/packages/app/client/src/commands/emulatorCommands.spec.ts +++ b/packages/app/client/src/commands/emulatorCommands.spec.ts @@ -40,7 +40,7 @@ import { bot } from '../data/reducer/bot'; import { chat } from '../data/reducer/chat'; import { clientAwareSettings } from '../data/reducer/clientAwareSettingsReducer'; import { editor } from '../data/reducer/editor'; -import { RootState, store } from '../data/store'; +import { RootState } from '../data/store'; import { CommandServiceImpl } from '../platform/commands/commandServiceImpl'; import { registerCommands } from './emulatorCommands'; @@ -72,6 +72,7 @@ describe('The emulator commands', () => { cwd: 'path', locale: 'en-us', serverUrl: 'https://localhost', + debugMode: 1, }) ); }); @@ -157,4 +158,22 @@ describe('The emulator commands', () => { state = mockStore.getState(); expect(state.chat.changeKey).toBe(3); }); + + it('should open a chat file', async () => { + const callSpy = jest.spyOn(CommandServiceImpl, 'call').mockResolvedValue(true); + const remoteCallSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue(true); + + const { handler: openChatFileHandler } = registry.getCommand(SharedConstants.Commands.Emulator.OpenChatFile); + await openChatFileHandler('some/path.chat', true); + expect(remoteCallSpy).toHaveBeenCalledWith(SharedConstants.Commands.Emulator.OpenChatFile, 'some/path.chat'); + expect(callSpy).toHaveBeenCalledWith( + SharedConstants.Commands.Emulator.ReloadTranscript, + 'some/path.chat', + undefined, + { + activities: undefined, + inMemory: true, + } + ); + }); }); diff --git a/packages/app/client/src/commands/emulatorCommands.ts b/packages/app/client/src/commands/emulatorCommands.ts index 51cc489b4..0e3bf4409 100644 --- a/packages/app/client/src/commands/emulatorCommands.ts +++ b/packages/app/client/src/commands/emulatorCommands.ts @@ -30,10 +30,12 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // - -import { newNotification, SharedConstants } from '@bfemulator/app-shared'; -import { Activity, CommandRegistryImpl, isLocalHostUrl, uniqueId } from '@bfemulator/sdk-shared'; +// import base64Url from 'base64url'; +// import { createDirectLine } from 'botframework-webchat'; +import { DebugMode, newNotification, SharedConstants } from '@bfemulator/app-shared'; +import { CommandRegistryImpl, isLocalHostUrl, uniqueId } from '@bfemulator/sdk-shared'; import { IEndpointService } from 'botframework-config/lib/schema'; +import { Activity } from 'botframework-schema'; import * as Constants from '../constants'; import * as ChatActions from '../data/action/chatActions'; @@ -54,7 +56,12 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) { // Open a new emulator tabbed document commandRegistry.registerCommand( Emulator.NewLiveChat, - (endpoint: IEndpointService, focusExistingChat: boolean = false, conversationId: string) => { + ( + endpoint: IEndpointService, + focusExistingChat: boolean = false, + conversationId: string, + mode: ChatActions.ChatMode = 'livechat' + ) => { const state = store.getState(); let documentId: string; @@ -70,15 +77,23 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) { if (!documentId) { documentId = uniqueId(); const { currentUserId } = state.clientAwareSettings.users; - store.dispatch( - ChatActions.newDocument(documentId, 'livechat', { - botId: 'bot', - endpointId: endpoint.id, - endpointUrl: endpoint.endpoint, - userId: currentUserId, - conversationId, - }) - ); + const action = ChatActions.newChat(documentId, mode, { + botId: 'bot', + endpointId: endpoint.id, + endpointUrl: endpoint.endpoint, + userId: currentUserId, + conversationId, + // directLine: createDirectLine({ + // secret: base64Url.encode(JSON.stringify({ conversationId, endpointId: endpoint.id })), + // domain: `${ state.clientAwareSettings.serverUrl }/v3/directline`, + // webSocket: false, + // }) + }); + if (state.clientAwareSettings.debugMode === DebugMode.Sidecar) { + action.payload.ui.horizontalSplitter[0].percentage = 75; + action.payload.ui.verticalSplitter[0].percentage = 25; + } + store.dispatch(action); } if (!isLocalHostUrl(endpoint.endpoint)) { @@ -105,7 +120,7 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) { const { currentUserId } = store.getState().clientAwareSettings.users; if (!tabGroup) { store.dispatch( - ChatActions.newDocument(filePath, 'transcript', { + ChatActions.newChat(filePath, 'transcript', { ...additionalData, botId: 'bot', userId: currentUserId, @@ -165,7 +180,7 @@ export function registerCommands(commandRegistry: CommandRegistryImpl) { store.dispatch(ChatActions.closeDocument(filePath)); } store.dispatch( - ChatActions.newDocument(filePath, 'transcript', { + ChatActions.newChat(filePath, 'transcript', { ...additionalData, botId: 'bot', userId: currentUserId, diff --git a/packages/app/client/src/commands/settingsCommands.ts b/packages/app/client/src/commands/settingsCommands.ts index d44e5aecb..40a7a6d80 100644 --- a/packages/app/client/src/commands/settingsCommands.ts +++ b/packages/app/client/src/commands/settingsCommands.ts @@ -41,7 +41,8 @@ import { store } from '../data/store'; export function registerCommands(commandRegistry: CommandRegistryImpl) { const { Settings } = SharedConstants.Commands; - commandRegistry.registerCommand(Settings.ReceiveGlobalSettings, (settings: ClientAwareSettings) => { + commandRegistry.registerCommand(Settings.ReceiveGlobalSettings, async (settings: ClientAwareSettings) => { store.dispatch(clientAwareSettingsChanged(settings)); + await commandRegistry.getCommand(SharedConstants.Commands.UI.SwitchDebugMode).handler(settings.debugMode); }); } diff --git a/packages/app/client/src/commands/uiCommands.spec.ts b/packages/app/client/src/commands/uiCommands.spec.ts index d69d0a39a..fa4b21d13 100644 --- a/packages/app/client/src/commands/uiCommands.spec.ts +++ b/packages/app/client/src/commands/uiCommands.spec.ts @@ -32,6 +32,7 @@ // import { SharedConstants } from '@bfemulator/app-shared'; import { CommandRegistryImpl } from '@bfemulator/sdk-shared'; +import { DebugMode } from '@bfemulator/app-shared'; import { CONTENT_TYPE_APP_SETTINGS, DOCUMENT_ID_APP_SETTINGS } from '../constants'; import { AzureAuthAction, AzureAuthWorkflow, invalidateArmToken } from '../data/action/azureAuthActions'; @@ -48,6 +49,9 @@ import { SecretPromptDialogContainer, } from '../ui/dialogs'; import { CommandServiceImpl } from '../platform/commands/commandServiceImpl'; +import { BotActionType } from '../data/action/botActions'; +import { ExplorerActions } from '../data/action/explorerActions'; +import { SWITCH_DEBUG_MODE } from '../data/action/debugModeAction'; import { registerCommands } from './uiCommands'; @@ -145,4 +149,17 @@ describe('the uiCommands', () => { themeName: 'light', }); }); + + it('should orchestrate the switch to sidecar debug mode', async () => { + const dispatchedActions = []; + store.dispatch = action => { + dispatchedActions.push(action); + return action; + }; + registry.getCommand(Commands.SwitchDebugMode).handler(DebugMode.Sidecar); + expect(dispatchedActions.length).toBe(3); + [BotActionType.close, ExplorerActions.Show, SWITCH_DEBUG_MODE].forEach((type, index) => + expect(type).toEqual(dispatchedActions[index].type) + ); + }); }); diff --git a/packages/app/client/src/commands/uiCommands.ts b/packages/app/client/src/commands/uiCommands.ts index 90b3b9908..f94e935ca 100644 --- a/packages/app/client/src/commands/uiCommands.ts +++ b/packages/app/client/src/commands/uiCommands.ts @@ -31,17 +31,19 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { SharedConstants } from '@bfemulator/app-shared'; +import { DebugMode, SharedConstants } from '@bfemulator/app-shared'; import { CommandRegistry } from '@bfemulator/sdk-shared'; import { ServiceTypes } from 'botframework-config/lib/schema'; import * as Constants from '../constants'; import { azureArmTokenDataChanged, beginAzureAuthWorkflow, invalidateArmToken } from '../data/action/azureAuthActions'; +import { closeBot } from '../data/action/botActions'; +import { switchDebugMode } from '../data/action/debugModeAction'; import * as EditorActions from '../data/action/editorActions'; import * as NavBarActions from '../data/action/navBarActions'; import { ProgressIndicatorPayload, updateProgressIndicator } from '../data/action/progressIndicatorActions'; import { switchTheme } from '../data/action/themeActions'; -import { showWelcomePage } from '../data/editorHelpers'; +import { getTabGroupForDocument, showWelcomePage } from '../data/editorHelpers'; import { AzureAuthState } from '../data/reducer/azureAuthReducer'; import { store } from '../data/store'; import { CommandServiceImpl } from '../platform/commands/commandServiceImpl'; @@ -58,6 +60,9 @@ import { UpdateAvailableDialogContainer, UpdateUnavailableDialogContainer, } from '../ui/dialogs'; +import * as ExplorerActions from '../data/action/explorerActions'; +import { closeConversation } from '../data/action/chatActions'; +import { close } from '../data/action/editorActions'; /** Register UI commands (toggling UI) */ export function registerCommands(commandRegistry: CommandRegistry) { @@ -125,7 +130,27 @@ export function registerCommands(commandRegistry: CommandRegistry) { store.dispatch(switchTheme(themeName, themeComponents)); CommandServiceImpl.remoteCall(Telemetry.TrackEvent, 'app_chooseTheme', { themeName, - }).catch(_e => void 0); + }).catch(); + }); + + // --------------------------------------------------------------------------- + // Debug mode from main + commandRegistry.registerCommand(UI.SwitchDebugMode, (debugMode: DebugMode) => { + const { + editor: { editors, activeEditor }, + } = store.getState(); + const { documents } = editors[activeEditor]; + store.dispatch(closeBot()); + store.dispatch(ExplorerActions.showExplorer(debugMode !== DebugMode.Sidecar)); + store.dispatch(switchDebugMode(debugMode)); + // Close all active conversations - this is a clean wipe of all active conversations + Object.values(documents).forEach(document => { + if (!document.isGlobal) { + const { documentId } = document; + store.dispatch(close(getTabGroupForDocument(documentId), documentId)); + store.dispatch(closeConversation(documentId)); + } + }); }); // --------------------------------------------------------------------------- @@ -152,7 +177,7 @@ export function registerCommands(commandRegistry: CommandRegistry) { // --------------------------------------------------------------------------- // Show post migration dialog on startup if the user has just been migrated commandRegistry.registerCommand(UI.ShowPostMigrationDialog, () => { - DialogService.showDialog(PostMigrationDialogContainer); + return DialogService.showDialog(PostMigrationDialogContainer); }); // --------------------------------------------------------------------------- diff --git a/packages/app/client/src/data/action/botActions.ts b/packages/app/client/src/data/action/botActions.ts index dec2fce49..3add5bd27 100644 --- a/packages/app/client/src/data/action/botActions.ts +++ b/packages/app/client/src/data/action/botActions.ts @@ -107,7 +107,7 @@ export function openBotViaUrlAction( ): BotAction> { return { type: BotActionType.openViaUrl, - payload: params, + payload: { ...params, mode: 'livechat-url' }, }; } diff --git a/packages/app/client/src/data/action/chatActions.ts b/packages/app/client/src/data/action/chatActions.ts index 88eccd58b..b4f2712fa 100644 --- a/packages/app/client/src/data/action/chatActions.ts +++ b/packages/app/client/src/data/action/chatActions.ts @@ -33,36 +33,55 @@ import { LogEntry } from '@bfemulator/sdk-shared'; import { Action } from 'redux'; +import { Activity } from 'botframework-schema'; export enum ChatActions { activeInspectorChanged = 'CHAT/INSPECTOR/CHANGED', newChat = 'CHAT/DOCUMENT/NEW', openChat = 'CHAT/DOCUMENT/OPEN', - closeChat = 'CHAT/DOCUMENT/CLOSE', + closeConversation = 'CHAT/CLOSE', + closeDocument = 'CHAT/DOCUMENT/CLOSE', newConversation = 'CHAT/CONVERSATION/NEW', appendLog = 'CHAT/LOG/APPEND', clearLog = 'CHAT/LOG/CLEAR', setInspectorObjects = 'CHAT/INSPECTOR/OBJECTS/SET', + setHighlightedObjects = 'CHAT/HIGHLIGHTED/OBJECTS/SET', addTranscript = 'CHAT/TRANSCRIPT/ADD', clearTranscripts = 'CHAT/TRANSCRIPT/CLEAR', removeTranscript = 'CHAT/TRANSCRIPT/REMOVE', updateChat = 'CHAT/DOCUMENT/UPDATE', + showContextMenuForActivity = 'CHAT/CONTEXT_MENU/SHOW', + webSpeechFactoryUpdated = 'CHAT/SPEECH/TOKEN/RETRIEVED', + webChatStoreUpdated = 'CHAT/STORE/UPDATED', + updatePendingSpeechTokenRetrieval = 'CHAT/SPEECH/TOKEN/PENDING/UPDATE', } export interface ActiveInspectorChangedPayload { inspectorWebView: HTMLWebViewElement; } -export interface NewChatAction { - type: ChatActions.newChat; - payload: { - [propName: string]: any; - documentId: string; - mode: ChatMode; - }; +export interface NewChatPayload { + [propName: string]: any; + + documentId: string; + mode: ChatMode; +} + +export interface WebSpeechFactoryPayload { + documentId: string; + factory: () => any; } -export interface CloseChatPayload { +export interface WebChatStorePayload { + documentId: string; + store: any; +} + +export interface PendingSpeechTokenRetrievalPayload { + pending: boolean; +} + +export interface DocumentIdPayload { documentId: string; } @@ -85,6 +104,11 @@ export interface SetInspectorObjectsPayload { objs: any; } +export interface SetHighlightedObjectsPayload { + documentId: string; + objs: Activity[]; +} + export interface AddTranscriptPayload extends RemoveTranscriptPayload {} export interface RemoveTranscriptPayload { @@ -100,7 +124,7 @@ export interface ChatAction extends Action { payload: T; } -type ChatMode = 'livechat' | 'transcript'; +export type ChatMode = 'livechat' | 'transcript' | 'livechat-url'; export function inspectorChanged(inspectorWebView: HTMLWebViewElement): ChatAction { return { @@ -134,7 +158,28 @@ export function removeTranscript(filename: string): ChatAction any): ChatAction { + return { + type: ChatActions.webSpeechFactoryUpdated, + payload: { documentId, factory }, + }; +} + +export function webChatStoreUpdated(documentId: string, store: any): ChatAction { + return { + type: ChatActions.webChatStoreUpdated, + payload: { documentId, store }, + }; +} + +export function updatePendingSpeechTokenRetrieval(pending: boolean): ChatAction { + return { + type: ChatActions.updatePendingSpeechTokenRetrieval, + payload: { pending }, + }; +} + +export function newChat(documentId: string, mode: ChatMode, additionalData?: object): ChatAction { return { type: ChatActions.newChat, payload: { @@ -173,9 +218,18 @@ export function newDocument(documentId: string, mode: ChatMode, additionalData?: }; } -export function closeDocument(documentId: string): ChatAction { +export function closeDocument(documentId: string): ChatAction { + return { + type: ChatActions.closeDocument, + payload: { + documentId, + }, + }; +} + +export function closeConversation(documentId: string): ChatAction { return { - type: ChatActions.closeChat, + type: ChatActions.closeConversation, payload: { documentId, }, @@ -222,6 +276,20 @@ export function setInspectorObjects(documentId: string, objs: any): ChatAction { + objs = Array.isArray(objs) ? objs : [objs]; + return { + type: ChatActions.setHighlightedObjects, + payload: { + documentId, + objs, + }, + }; +} + export function updateChat(documentId: string, updatedValues: any): ChatAction { return { type: ChatActions.updateChat, @@ -231,3 +299,10 @@ export function updateChat(documentId: string, updatedValues: any): ChatAction): ChatAction> { + return { + type: ChatActions.showContextMenuForActivity, + payload: activity, + }; +} diff --git a/packages/sdk/shared/src/types/activity/invoke.ts b/packages/app/client/src/data/action/debugModeAction.ts similarity index 75% rename from packages/sdk/shared/src/types/activity/invoke.ts rename to packages/app/client/src/data/action/debugModeAction.ts index f30cc5bc9..e72aace1d 100644 --- a/packages/sdk/shared/src/types/activity/invoke.ts +++ b/packages/app/client/src/data/action/debugModeAction.ts @@ -30,22 +30,23 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +import { DebugMode } from '@bfemulator/app-shared'; -import { ChannelAccount, ConversationAccount } from '../account'; +export const SWITCH_DEBUG_MODE = 'switchDebugMode'; +export type DebugType = 'switchDebugMode'; -import { Activity } from './activity'; +export interface DebugModeAction { + type: DebugType; + payload: T; +} -export interface ConversationReference { - activityId: string; - bot: ChannelAccount; - channelId: string; - conversation: ConversationAccount; - serviceUrl: string; - user: ChannelAccount; +export interface SwitchDebugModePayload { + debugMode: DebugMode; } -export interface InvokeActivity extends Activity { - name?: string; - relatesTo?: ConversationReference; - value?: any; +export function switchDebugMode(debugMode: DebugMode): DebugModeAction { + return { + type: SWITCH_DEBUG_MODE, + payload: { debugMode }, + }; } diff --git a/packages/app/client/src/data/reducer/chat.spec.ts b/packages/app/client/src/data/reducer/chat.spec.ts index 98c7bced8..addd559c8 100644 --- a/packages/app/client/src/data/reducer/chat.spec.ts +++ b/packages/app/client/src/data/reducer/chat.spec.ts @@ -41,7 +41,7 @@ import { clearTranscripts, closeDocument, newConversation, - newDocument, + newChat, removeTranscript, setInspectorObjects, updateChat, @@ -99,14 +99,14 @@ describe('Chat reducer tests', () => { it('should create a new chat', () => { const newChatName = 'newChat'; - const action = newDocument(newChatName, 'livechat'); + const action = newChat(newChatName, 'livechat'); const state = chat(DEFAULT_STATE, action); expect(state.changeKey).toBe(1); expect(state.chats[newChatName]).toBeTruthy(); }); it('should close a chat', () => { - let state = chat(DEFAULT_STATE, newDocument(testChatId, 'livechat')); + let state = chat(DEFAULT_STATE, newChat(testChatId, 'livechat')); const action = closeDocument(testChatId); state = chat(DEFAULT_STATE, action); expect(state.chats[testChatId]).toBeFalsy(); diff --git a/packages/app/client/src/data/reducer/chat.ts b/packages/app/client/src/data/reducer/chat.ts index d3e4cb53a..bbd6a5686 100644 --- a/packages/app/client/src/data/reducer/chat.ts +++ b/packages/app/client/src/data/reducer/chat.ts @@ -31,20 +31,32 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { ChatAction, ChatActions } from '../action/chatActions'; +import { + ChatAction, + ChatActions, + PendingSpeechTokenRetrievalPayload, + WebChatStorePayload, + WebSpeechFactoryPayload, +} from '../action/chatActions'; import { EditorAction, EditorActions } from '../action/editorActions'; export interface ChatState { changeKey?: number; // TODO: keys should map to an Chat chats?: { [chatId: string]: any }; + webSpeechFactories?: { [documentId: string]: () => any }; + webChatStores: { [documentId: string]: any }; transcripts?: string[]; + pendingSpeechTokenRetrieval: boolean; } const DEFAULT_STATE: ChatState = { changeKey: 0, chats: {}, transcripts: [], + webSpeechFactories: {}, + webChatStores: {}, + pendingSpeechTokenRetrieval: false, }; export function chat(state: ChatState = DEFAULT_STATE, action: ChatAction | EditorAction): ChatState { @@ -84,14 +96,43 @@ export function chat(state: ChatState = DEFAULT_STATE, action: ChatAction | Edit break; } - case ChatActions.closeChat: { - const { payload } = action; + case ChatActions.webSpeechFactoryUpdated: + { + const { documentId, factory } = action.payload as WebSpeechFactoryPayload; + const { webSpeechFactories } = state; + state = { + ...state, + webSpeechFactories: { ...webSpeechFactories, [documentId]: factory }, + }; + } + break; + + case ChatActions.webChatStoreUpdated: + { + const { documentId, store } = action.payload as WebChatStorePayload; + const { webChatStores } = state; + state = { + ...state, + webChatStores: { ...webChatStores, [documentId]: store }, + }; + } + break; + + case ChatActions.updatePendingSpeechTokenRetrieval: + state = { + ...state, + pendingSpeechTokenRetrieval: (action.payload as PendingSpeechTokenRetrievalPayload).pending, + }; + break; + + case ChatActions.closeDocument: { + const { documentId } = action.payload; // can't use the JSON.parse(JSON.stringify()) // trick with chats because Subscribers are circular - if (payload.documentId in state.chats) { + if (documentId in state.chats) { const copy = { ...state }; copy.changeKey += 1; - delete copy.chats[payload.documentId]; + delete copy.chats[documentId]; state = { ...copy }; } break; @@ -165,6 +206,28 @@ export function chat(state: ChatState = DEFAULT_STATE, action: ChatAction | Edit break; } + case ChatActions.setHighlightedObjects: + { + const { payload } = action; + let document = state.chats[payload.documentId]; + if (document) { + document = { + ...document, + highlightedObjects: payload.objs, + }; + } + state = { + ...state, + chats: { + ...state.chats, + [payload.documentId]: { + ...document, + }, + }, + }; + } + break; + case ChatActions.setInspectorObjects: { const { payload } = action; let document = state.chats[payload.documentId]; diff --git a/packages/app/client/src/data/reducer/clientAwareSettingsReducer.ts b/packages/app/client/src/data/reducer/clientAwareSettingsReducer.ts index 368d148c7..34996a470 100644 --- a/packages/app/client/src/data/reducer/clientAwareSettingsReducer.ts +++ b/packages/app/client/src/data/reducer/clientAwareSettingsReducer.ts @@ -38,11 +38,8 @@ export function clientAwareSettings( state: ClientAwareSettings = {} as any, action: ClientAwareSettingsActions ): ClientAwareSettings { - switch (action.type) { - case CLIENT_AWARE_SETTINGS_CHANGED: - return { ...state, ...action.payload }; - - default: - return state; + if (action.type === CLIENT_AWARE_SETTINGS_CHANGED) { + return { ...state, ...action.payload }; } + return state; } diff --git a/packages/app/client/src/data/reducer/themeReducer.ts b/packages/app/client/src/data/reducer/themeReducer.ts index 6eae0cf77..b06ccc604 100644 --- a/packages/app/client/src/data/reducer/themeReducer.ts +++ b/packages/app/client/src/data/reducer/themeReducer.ts @@ -30,7 +30,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -import { SwitchThemePayload, ThemeAction } from '../action/themeActions'; +import { SWITCH_THEME, SwitchThemePayload, ThemeAction } from '../action/themeActions'; export interface ThemeState { themeName: string; @@ -46,7 +46,7 @@ export const initialState: ThemeState = { export function theme(state: ThemeState = initialState, action: ThemeAction): ThemeState { switch (action.type) { - case 'switchTheme': + case SWITCH_THEME: return { ...state, ...action.payload }; default: diff --git a/packages/app/client/src/data/sagas/botSagas.ts b/packages/app/client/src/data/sagas/botSagas.ts index 90f69fe74..b7ca7eb0e 100644 --- a/packages/app/client/src/data/sagas/botSagas.ts +++ b/packages/app/client/src/data/sagas/botSagas.ts @@ -71,11 +71,17 @@ export function* openBotViaUrl(action: BotAction state.clientAwareSettings.users); action.payload.user = users.usersById[users.currentUserId]; } - const response = yield ConversationService.startConversation(serverUrl, action.payload); - if (!response.ok) { - const errorNotification = beginAdd( - newNotification(`An Error occurred opening the bot at ${action.payload.endpoint}: ${response.statusText}`) - ); + let error; + try { + const response = yield ConversationService.startConversation(serverUrl, action.payload); + if (!response.ok) { + error = `An Error occurred opening the bot at ${action.payload.endpoint}: ${response.statusText}`; + } + } catch (e) { + error = e.message; + } + if (error) { + const errorNotification = beginAdd(newNotification(error)); yield put(errorNotification); } } diff --git a/packages/app/client/src/data/sagas/chatSagas.spec.ts b/packages/app/client/src/data/sagas/chatSagas.spec.ts new file mode 100644 index 000000000..23369ee2c --- /dev/null +++ b/packages/app/client/src/data/sagas/chatSagas.spec.ts @@ -0,0 +1,740 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// +// Microsoft Bot Framework: http://botframework.com +// +// Bot Framework Emulator Github: +// https://github.com/Microsoft/BotFramwork-Emulator +// +// Copyright (c) Microsoft Corporation +// All rights reserved. +// +// MIT License: +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +import { applyMiddleware, combineReducers, createStore } from 'redux'; +import sagaMiddlewareFactory from 'redux-saga'; +import { ActivityTypes } from 'botframework-schema'; +import * as Electron from 'electron'; +import { SharedConstants } from '@bfemulator/app-shared'; + +import { bot } from '../reducer/bot'; +import { chat } from '../reducer/chat'; +import { editor } from '../reducer/editor'; +import { presentation } from '../reducer/presentation'; +import * as Constants from '../../constants'; +import { CommandServiceImpl } from '../../platform/commands/commandServiceImpl'; +import { closeConversation, newChat, showContextMenuForActivity } from '../action/chatActions'; + +import { chatSagas } from './chatSagas'; + +const sagaMiddleWare = sagaMiddlewareFactory(); +let mockStore; +let mockStoreState; +jest.mock('../../ui/dialogs', () => ({})); +jest.mock('../store', () => ({ + get store() { + return mockStore; + }, +})); + +jest.mock('electron', () => { + return { + clipboard: { writeText: () => true }, + }; +}); + +jest.mock('botframework-webchat', () => { + return { + createCognitiveServicesBingSpeechPonyfillFactory: () => () => 'Yay! ponyfill!', + }; +}); + +const log = { + entries: [ + { + items: [ + { + payload: { + obj: { + channelId: 'emulator', + conversation: { + conversationType: 'personal', + id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat', + }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a075a350-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:41-07:00', + locale: 'en', + name: 'BotState', + recipient: { + id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', + role: 'user', + }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:41.957Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: [ + { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { + instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d', + }, + }, + }, + { + id: 'slot-dialog', + state: { + slot: 'fullname', + values: {}, + }, + }, + { + id: 'fullname', + state: { + slot: 'first', + values: {}, + }, + }, + { + id: 'text', + state: { + options: { + prompt: 'Please enter your first name.', + }, + state: {}, + }, + }, + ], + userState: {}, + }, + eTag: '2', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/botState', + }, + text: 'trace', + }, + type: 'inspectable-object', + }, + { + payload: { + obj: { + channelId: 'emulator', + conversation: { + conversationType: 'personal', + id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat', + }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a075a350-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:41-07:00', + locale: 'en', + name: 'BotState', + recipient: { + id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', + role: 'user', + }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:41.957Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: [ + { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { + instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d', + }, + }, + }, + { + id: 'slot-dialog', + state: { + slot: 'fullname', + values: {}, + }, + }, + { + id: 'fullname', + state: { + slot: 'first', + values: {}, + }, + }, + { + id: 'text', + state: { + options: { + prompt: 'Please enter your first name.', + }, + state: {}, + }, + }, + ], + userState: {}, + }, + eTag: '2', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/botState', + }, + }, + type: 'summary-text', + }, + ], + timestamp: 1554407741957, + }, + { + items: [ + { + payload: { + obj: { + channelId: 'emulator', + conversation: { + conversationType: 'personal', + id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat', + }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a498c020-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:48-07:00', + locale: 'en', + name: 'BotState', + recipient: { + id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', + role: 'user', + }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:48.898Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: [ + { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { + instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d', + }, + }, + }, + { + id: 'slot-dialog', + state: { + slot: 'fullname', + values: {}, + }, + }, + { + id: 'fullname', + state: { + slot: 'last', + values: { + first: 'Justin ', + }, + }, + }, + { + id: 'text', + state: { + options: { + prompt: 'Please enter your last name.', + }, + state: {}, + }, + }, + ], + userState: {}, + }, + eTag: '3', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/botState', + }, + text: 'trace', + }, + type: 'inspectable-object', + }, + { + payload: { + obj: { + channelId: 'emulator', + conversation: { + conversationType: 'personal', + id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat', + }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a498c020-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:48-07:00', + locale: 'en', + name: 'BotState', + recipient: { + id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', + role: 'user', + }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:48.898Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: [ + { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { + instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d', + }, + }, + }, + { + id: 'slot-dialog', + state: { + slot: 'fullname', + values: {}, + }, + }, + { + id: 'fullname', + state: { + slot: 'last', + values: { + first: 'Justin ', + }, + }, + }, + { + id: 'text', + state: { + options: { + prompt: 'Please enter your last name.', + }, + state: {}, + }, + }, + ], + userState: {}, + }, + eTag: '3', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/botState', + }, + }, + type: 'summary-text', + }, + ], + timestamp: 1554407748898, + }, + { + items: [ + { + payload: { + obj: { + channelId: 'emulator', + conversation: { + conversationType: 'personal', + id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat', + }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a65d2c70-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:51-07:00', + locale: 'en', + name: 'BotState', + recipient: { + id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', + role: 'user', + }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:51.863Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: [ + { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { + instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d', + }, + }, + }, + { + id: 'slot-dialog', + state: { + slot: 'age', + values: { + fullname: { + slot: 'last', + values: { + first: 'Justin ', + last: 'Wilaby', + }, + }, + }, + }, + }, + { + id: 'number', + state: { + options: { + prompt: 'Please enter your age.', + }, + state: {}, + }, + }, + ], + userState: {}, + }, + eTag: '4', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/botState', + }, + text: 'trace', + }, + type: 'inspectable-object', + }, + { + payload: { + obj: { + channelId: 'emulator', + conversation: { + conversationType: 'personal', + id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat', + }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a65d2c70-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:51-07:00', + locale: 'en', + name: 'BotState', + recipient: { + id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', + role: 'user', + }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:51.863Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: [ + { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { + instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d', + }, + }, + }, + { + id: 'slot-dialog', + state: { + slot: 'age', + values: { + fullname: { + slot: 'last', + values: { + first: 'Justin ', + last: 'Wilaby', + }, + }, + }, + }, + }, + { + id: 'number', + state: { + options: { + prompt: 'Please enter your age.', + }, + state: {}, + }, + }, + ], + userState: {}, + }, + eTag: '4', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/botState', + }, + }, + type: 'summary-text', + }, + ], + timestamp: 1554407751863, + }, + ], +}; + +describe('The ChatSagas,', () => { + beforeEach(() => { + mockStoreState = { + bot: { + activeBot: { + services: [{ id: 'endpoint2', appId: 'anAppId', appPassword: 'anAppPassword' }], + }, + }, + chat: { + chats: { + doc1: { + log, + conversationId: 'convo1', + documentId: 'doc1', + endpointId: 'endpoint1', + userId: 'someUserId', + }, + }, + }, + editor: { + activeEditor: Constants.EDITOR_KEY_PRIMARY, + editors: { + [Constants.EDITOR_KEY_PRIMARY]: { + activeDocumentId: 'doc1', + }, + }, + }, + presentation: { enabled: true }, + }; + + mockStore = createStore( + combineReducers({ + bot, + chat, + editor, + presentation, + }), + mockStoreState, + applyMiddleware(sagaMiddleWare) + ); + sagaMiddleWare.run(chatSagas); + }); + + describe('when showing a context menu for an activity', () => { + it('should handle the "copy message" selection', async () => { + const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue({ id: 'copy' }); + const clipboardSpy = jest.spyOn(Electron.clipboard, 'writeText'); + const activity = { + valueType: '', + type: ActivityTypes.Trace, + value: { type: ActivityTypes.Message, text: 'Hello Bot!' }, + }; + mockStore.dispatch(showContextMenuForActivity(activity)); + await Promise.resolve(true); + expect(commandServiceSpy).toHaveBeenCalled(); + expect(clipboardSpy).toHaveBeenCalledWith('Hello Bot!'); + }); + + it('should handle the "copy json" selection', async () => { + const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue({ id: 'json' }); + const clipboardSpy = jest.spyOn(Electron.clipboard, 'writeText'); + const activity = { + valueType: '', + type: ActivityTypes.Trace, + value: { type: ActivityTypes.Message, text: 'Hello Bot!' }, + }; + mockStore.dispatch(showContextMenuForActivity(activity)); + await Promise.resolve(true); + expect(commandServiceSpy).toHaveBeenCalled(); + expect(clipboardSpy).toHaveBeenCalledWith(JSON.stringify(activity, null, 2)); + }); + + it('should handle the "Compare with previous" selection', async () => { + const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue({ id: 'diff' }); + const activity = mockStoreState.chat.chats.doc1.log.entries[2].items[0].payload.obj; + mockStore.dispatch(showContextMenuForActivity(activity)); + await Promise.resolve(true); + expect(commandServiceSpy).toHaveBeenCalled(); + const state = mockStore.getState(); + expect(JSON.stringify(state.chat.chats.doc1.inspectorObjects)).toEqual( + JSON.stringify([ + { + channelId: 'emulator', + conversation: { conversationType: 'personal', id: '9fb93120-5713-11e9-a20f-e185020ba18b|livechat' }, + from: { + aadObjectId: '8d81b1c4-a057-4d27-a41d-e40b3105e6ee', + id: '29:1roELw8-HUdxuNSlGwtGqacHW_y-tsmLhvs42duabIDv0JFovw3WX7QC-syrrAYRt0RHBqoS1i0Mt18un1YZmyw', + name: 'Justin Wilaby', + role: 'user', + }, + id: 'a65d2c70-5713-11e9-a20f-e185020ba18b', + label: 'Bot State', + localTimestamp: '2019-04-04T12:55:51-07:00', + locale: 'en', + name: 'BotState', + recipient: { id: '28:825059e1-0dd5-4a90-9136-121a702c18ca', role: 'user' }, + serviceUrl: 'http://localhost:9000', + timestamp: '2019-04-04T19:55:51.863Z', + type: 'trace', + value: { + conversationState: { + dialogState: { + conversationState: {}, + dialogStack: { + '0': { + id: 'root', + state: { + options: {}, + stepIndex: 0, + values: { instanceId: '6938f312-523a-2db2-92ba-9680f559dd2d' }, + }, + }, + '1': { + id: 'slot-dialog', + state: { + values: { + fullname: { + slot: 'last', + values: { first: 'Justin ', last: 'Wilaby' }, + }, + }, + '+slot': 'age', + '-slot': 'fullname', + }, + }, + '2': { + state: { + options: { prompt: 'Please enter your age.' }, + state: {}, + '-slot': 'last', + '-values': { first: 'Justin ' }, + }, + '+id': 'number', + '-id': 'fullname', + }, + '-3': { + id: 'text', + state: { options: { prompt: 'Please enter your last name.' }, state: {} }, + }, + }, + userState: {}, + }, + '+eTag': '4', + '-eTag': '3', + }, + userState: {}, + }, + valueType: 'https://www.botframework.com/schemas/diff', + }, + ]) + ); + }); + }); + + it('when closing a document it should notify the main process so it can remove the conversation', async () => { + const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue(true); + mockStore.dispatch(closeConversation('doc1')); + expect(commandServiceSpy).toHaveBeenCalledWith(SharedConstants.Commands.Emulator.DeleteConversation, 'convo1'); + await Promise.resolve(); // wait for the comand service call to complete + expect(mockStore.getState().chat.chats.doc1).toBeUndefined(); + }); + + it('when starting a new conversation, should create a speech token ponyfill factory', async () => { + const commandServiceSpy = jest.spyOn(CommandServiceImpl, 'remoteCall').mockResolvedValue('mockSpeechToken'); + + mockStore.dispatch( + newChat('doc2', 'livechat', { + conversationId: 'convo2', + endpointId: 'endpoint2', + userId: 'someUserId2', + }) + ); + + await Promise.resolve(); + const state = mockStore.getState(); + expect(state.chat.chats.doc2).not.toBeUndefined(); + expect(state.chat.webSpeechFactories.doc2).not.toBeUndefined(); + expect(state.chat.webSpeechFactories.doc2()).toBe('Yay! ponyfill!'); + expect(commandServiceSpy).toHaveBeenCalledWith( + SharedConstants.Commands.Emulator.GetSpeechToken, + 'endpoint2', + false + ); + }); +}); diff --git a/packages/app/client/src/data/sagas/chatSagas.ts b/packages/app/client/src/data/sagas/chatSagas.ts new file mode 100644 index 000000000..bb5d48a27 --- /dev/null +++ b/packages/app/client/src/data/sagas/chatSagas.ts @@ -0,0 +1,271 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. +// +// Microsoft Bot Framework: http://botframework.com +// +// Bot Framework Emulator Github: +// https://github.com/Microsoft/BotFramwork-Emulator +// +// Copyright (c) Microsoft Corporation +// All rights reserved. +// +// MIT License: +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// +import * as Electron from 'electron'; +import { MenuItemConstructorOptions } from 'electron'; +import { Activity, ActivityTypes } from 'botframework-schema'; +import { SharedConstants, ValueTypes } from '@bfemulator/app-shared'; +import { InspectableObjectLogItem, LogItem, LogItemType } from '@bfemulator/sdk-shared'; +import { diff } from 'deep-diff'; +import { IEndpointService } from 'botframework-config/lib/schema'; +import { createCognitiveServicesBingSpeechPonyfillFactory } from 'botframework-webchat'; +import { createStore as createWebChatStore } from 'botframework-webchat-core'; + +import { + ChatAction, + ChatActions, + closeDocument, + DocumentIdPayload, + NewChatPayload, + setHighlightedObjects, + setInspectorObjects, + updatePendingSpeechTokenRetrieval, + webChatStoreUpdated, + webSpeechFactoryUpdated, +} from '../action/chatActions'; +import { CommandServiceImpl } from '../../platform/commands/commandServiceImpl'; +import { RootState } from '../store'; +import { isSpeechEnabled } from '../../utils'; + +import { call, ForkEffect, put, select, takeEvery, takeLatest } from 'redux-saga/effects'; + +const getConversationIdFromDocumentId = (state: RootState, documentId: string) => { + return (state.chat.chats[documentId] || {}).conversationId; +}; + +const getWebSpeechFactoryForDocumentId = (state: RootState, documentId: string): (() => any) => { + return state.chat.webSpeechFactories[documentId]; +}; + +const getEndpointServiceByDocumentId = (state: RootState, documentId: string): IEndpointService => { + const chat = state.chat.chats[documentId]; + return ((state.bot.activeBot && state.bot.activeBot.services) || []).find( + s => s.id === chat.endpointId + ) as IEndpointService; +}; + +const getCurrentDocumentId = (state: RootState): string => { + const { editors, activeEditor } = state.editor; + const { activeDocumentId } = editors[activeEditor]; + return activeDocumentId; +}; + +const getPreviousBotState = (state: RootState, selectedTrace: Activity): Activity => { + const { editors, activeEditor } = state.editor; + const { activeDocumentId } = editors[activeEditor]; + const chat = state.chat.chats[activeDocumentId]; + const entries = chat.log.entries as any[]; + + const allEntries: LogItem[] = entries.reduce( + (agg: LogItem[], entry) => agg.concat(entry.items), + [] + ); + const filteredLogItems: LogItem[] = allEntries.filter( + (item: LogItem) => { + const activity = item.payload.obj as Activity; + return ( + item.type === LogItemType.InspectableObject && + activity.valueType === ValueTypes.BotState && + activity.id !== selectedTrace.id + ); + } + ); + const targetLogEntry = filteredLogItems.pop(); + return targetLogEntry && (targetLogEntry.payload.obj as Activity); +}; + +export function* showContextMenuForActivity(action: ChatAction): Iterable { + const { payload: activity } = action; + const previousBotState = yield select(getPreviousBotState, activity); + const diffEnabled = activity.valueType.endsWith('botState') && !!previousBotState; + const menuItems = [ + { label: 'Copy text', id: 'copy' }, + { label: 'Copy json', id: 'json' }, + { type: 'separator' }, + { label: 'Compare with previous', id: 'diff', enabled: diffEnabled }, + ] as MenuItemConstructorOptions[]; + + const { DisplayContextMenu } = SharedConstants.Commands.Electron; + const response: { id: string } = yield call( + CommandServiceImpl.remoteCall.bind(CommandServiceImpl), + DisplayContextMenu, + menuItems + ); + + if (!response) { + return; // canceled context menu + } + switch (response.id) { + case 'copy': + return Electron.clipboard.writeText(getTextFromActivity(activity)); + + case 'json': + return Electron.clipboard.writeText(JSON.stringify(activity, null, 2)); + + default: + yield* diffWithPreviousBotState(activity); + } +} + +export function* closeConversation(action: ChatAction): Iterable { + const conversationId = yield select(getConversationIdFromDocumentId, action.payload.documentId); + const { DeleteConversation } = SharedConstants.Commands.Emulator; + const { documentId } = action.payload; + yield call([CommandServiceImpl, CommandServiceImpl.remoteCall], DeleteConversation, conversationId); + yield put(closeDocument(documentId)); + // remove the webchat store when the document is closed + yield put(webChatStoreUpdated(documentId, null)); +} + +export function* newChat(action: ChatAction): Iterable { + const { documentId } = action.payload; + // Create a new webchat store for this documentId + yield put(webChatStoreUpdated(documentId, createWebChatStore())); + // Each time a new chat is open, retrieve the speech token + // if the endpoint is speech enabled and create a bind speech + // pony fill factory. This is consumed by WebChat... + yield put(webSpeechFactoryUpdated(documentId, null)); // remove the old factory + const endpoint: IEndpointService = yield select(getEndpointServiceByDocumentId, documentId); + if (!isSpeechEnabled(endpoint)) { + return; + } + yield put(updatePendingSpeechTokenRetrieval(true)); + // If an existing factory is found, refresh the token + const existingFactory: string = yield select(getWebSpeechFactoryForDocumentId, documentId); + const { GetSpeechToken: command } = SharedConstants.Commands.Emulator; + const token = yield call( + [CommandServiceImpl, CommandServiceImpl.remoteCall], + command, + endpoint.id, + !!existingFactory + ); + if (token) { + const factory = yield call(createCognitiveServicesBingSpeechPonyfillFactory, { + authorizationToken: token, + }); + yield put(webSpeechFactoryUpdated(documentId, factory)); // Provide the new factory to the store + } + yield put(updatePendingSpeechTokenRetrieval(false)); +} + +export function* diffWithPreviousBotState(currentBotState: Activity): Iterable { + const previousBotState: Activity = yield select(getPreviousBotState, currentBotState); + + const lhs = []; + const rhs = []; + const deltas = diff(previousBotState.value, currentBotState.value); + (deltas || []).forEach(diff => { + switch (diff.kind) { + case 'A': + { + const { item, path } = diff; + path.push(diff.index); + if (item.kind === 'D') { + lhs.push(path); + } else if (item.kind === 'E') { + rhs.push(path); + lhs.push(path); + } else { + rhs.push(path); + } + } + break; + + case 'D': + lhs.push(diff.path); + break; + + case 'E': + rhs.push(diff.path); + lhs.push(diff.path); + break; + + case 'N': + rhs.push(diff.path); + break; + } + }); + + // Clone the bot state and update the keys to show changes + const botStateClone: Activity = JSON.parse( + JSON.stringify(currentBotState, (key: string, value: any) => { + if (value instanceof Array) { + return Object.keys(value).reduce((conversion: any, key) => { + conversion['' + key] = value[key]; + return conversion; + }, {}); + } + return value; + }) + ); + botStateClone.valueType = 'https://www.botframework.com/schemas/diff'; + // values that were added + rhs.forEach(path => { + buildDiff('+', path, botStateClone.value, botStateClone.value); + }); + // values that were removed + lhs.forEach(path => { + buildDiff('-', path, botStateClone.value, previousBotState.value); + }); + const documentId = yield select(getCurrentDocumentId); + yield put(setHighlightedObjects(documentId, [previousBotState, currentBotState])); + yield put(setInspectorObjects(documentId, botStateClone)); +} + +function getTextFromActivity(activity: Activity): string { + if (activity.type === ActivityTypes.Trace) { + return 'text' in activity.value ? activity.value.text : activity.label; + } + return activity.text; +} + +function buildDiff(prependWith: string, path: (string | number)[], target: any, source: any): void { + let key; + for (let i = 0; i < path.length; i++) { + key = path[i]; + if (key in target && target[key] !== null && typeof target[key] === 'object') { + target = target[key]; + source = source[key]; + } else { + break; + } + } + const value = source[key]; + delete target[key]; + target[prependWith + key] = value; +} + +export function* chatSagas(): IterableIterator { + yield takeEvery(ChatActions.showContextMenuForActivity, showContextMenuForActivity); + yield takeEvery(ChatActions.closeConversation, closeConversation); + yield takeLatest([ChatActions.newChat, ChatActions.clearLog], newChat); +} diff --git a/packages/app/client/src/data/sagas/index.ts b/packages/app/client/src/data/sagas/index.ts index 32c3bc6dc..47bb884c5 100644 --- a/packages/app/client/src/data/sagas/index.ts +++ b/packages/app/client/src/data/sagas/index.ts @@ -41,10 +41,12 @@ import { notificationSagas } from './notificationSagas'; import { resourceSagas } from './resourcesSagas'; import { servicesExplorerSagas } from './servicesExplorerSagas'; import { welcomePageSagas } from './welcomePageSagas'; +import { chatSagas } from './chatSagas'; export const applicationSagas = [ azureAuthSagas, botSagas, + chatSagas, editorSagas, endpointSagas, frameworkSettingsSagas, diff --git a/packages/app/client/src/extensions.ts b/packages/app/client/src/extensions.ts index 79d77a227..37d992907 100644 --- a/packages/app/client/src/extensions.ts +++ b/packages/app/client/src/extensions.ts @@ -93,23 +93,21 @@ export class InspectorAPI { if (!Array.isArray(criterias)) { criterias = [criterias]; } - let canInspect = true; - criterias.forEach(criteria => { + return criterias.some(criteria => { // Path is a json-path const value = getValueFromPath(obj, criteria.path); if (typeof value === 'undefined') { - canInspect = false; + return false; } else { // Value can be a regex or a string literal if ((criteria.value || '').startsWith('/')) { const regex = new RegExp(criteria.value); - canInspect = canInspect && regex.test(value); + return regex.test(value); } else { - canInspect = canInspect && criteria.value === value; + return criteria.value === value; } } }); - return canInspect; } public static summaryText(inspector: ExtensionInspector, obj: any): string { diff --git a/packages/app/client/src/index.tsx b/packages/app/client/src/index.tsx index 0797bd436..c9e2f5333 100644 --- a/packages/app/client/src/index.tsx +++ b/packages/app/client/src/index.tsx @@ -64,10 +64,10 @@ ReactDOM.render( // Tell the main process we're loaded CommandServiceImpl.remoteCall(SharedConstants.Commands.ClientInit.Loaded) - .then(() => { + .then(async () => { showWelcomePage(); // do actions on main side that might open a document, so that they will be active over the welcome screen - CommandServiceImpl.remoteCall(SharedConstants.Commands.ClientInit.PostWelcomeScreen); + await CommandServiceImpl.remoteCall(SharedConstants.Commands.ClientInit.PostWelcomeScreen); window.addEventListener('keydown', globalHandlers, true); }) .catch(err => { diff --git a/packages/app/client/src/ui/dialogs/host/host.tsx b/packages/app/client/src/ui/dialogs/host/host.tsx index 62a2cb310..865d1cf80 100644 --- a/packages/app/client/src/ui/dialogs/host/host.tsx +++ b/packages/app/client/src/ui/dialogs/host/host.tsx @@ -46,10 +46,6 @@ export interface DialogHostProps { export class DialogHost extends React.Component { private _hostRef: HTMLElement; - constructor(props: DialogHostProps) { - super(props); - } - public componentDidMount() { this._hostRef.addEventListener('dialogRendered', this.initFocusTrap); } diff --git a/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.spec.tsx b/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.spec.tsx index 97d6c1e96..4f0fa990e 100644 --- a/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.spec.tsx +++ b/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.spec.tsx @@ -34,8 +34,6 @@ import * as React from 'react'; import { shallow } from 'enzyme'; -import { ChatContainer } from '../parts/chat/chatContainer'; - import ChatPanel from './chatPanel'; jest.mock('./chatPanel.scss', () => ({})); @@ -79,50 +77,8 @@ function render() { } describe('', () => { - beforeEach(() => { - document.selectedActivity$._listeners = []; - }); - - it('subscribes to selected activity changes only once', () => { - const component = render(); - expect(document.selectedActivity$._listeners).toHaveLength(1); - - component.setProps({ document }); - - expect(document.selectedActivity$._listeners).toHaveLength(1); - }); - - it('unsubscribes from the observable when unmounting', () => { + it('should render', () => { const component = render(); - - expect(document.selectedActivity$._listeners).toHaveLength(1); - - component.unmount(); - - expect(document.selectedActivity$._listeners).toHaveLength(0); - }); - - describe('updating the selected actvity', () => { - it('responds to observable changes', () => { - const component = render(); - - expect(component.state('selectedActivity')).toEqual(null); - - document.selectedActivity$.next({ new: 'activity' }); - - expect(component.state('selectedActivity')).toEqual({ new: 'activity' }); - }); - - it('passes an updater to the chat container', () => { - const component = render(); - const container = component.find(ChatContainer); - - (container.prop('updateSelectedActivity') as any)({ new: 'activity' }); - - expect(component.state('selectedActivity')).toEqual({ - new: 'activity', - showInInspector: true, - }); - }); + expect(component).not.toBeFalsy(); }); }); diff --git a/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.tsx b/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.tsx index 3fa38dfcc..ad7c06e25 100644 --- a/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.tsx +++ b/packages/app/client/src/ui/editor/emulator/chatPanel/chatPanel.tsx @@ -31,8 +31,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // import * as React from 'react'; -import { BehaviorSubject, Subscription } from 'rxjs'; -import { Activity } from '@bfemulator/sdk-shared'; import { ChatContainer } from '../parts/chat/chatContainer'; import { EmulatorMode } from '../emulator'; @@ -42,65 +40,21 @@ import * as styles from './chatPanel.scss'; interface ChatPanelProps { document?: { endpointUrl: string; - selectedActivity$?: BehaviorSubject; }; mode?: EmulatorMode; onStartConversation?: () => any; className?: string; } -interface ChatPanelState { - selectedActivity: any | null; -} - -export default class ChatPanel extends React.Component { - state = { - selectedActivity: null, - }; - - selectedActivitySub: Subscription; - - componentDidUpdate() { - const { document } = this.props; - - if (document && document.selectedActivity$ && !this.selectedActivitySub) { - this.selectedActivitySub = document.selectedActivity$.subscribe(selectedActivity => - this.setState({ selectedActivity }) - ); - } - } - - componentWillUnmount() { - if (this.selectedActivitySub) { - this.selectedActivitySub.unsubscribe(); - } - } - - updateSelectedActivityObservable = (observable: BehaviorSubject) => { - return (activity: Activity) => { - if (observable) { - observable.next({ - ...activity, - showInInspector: true, - }); - } - }; - }; - +export default class ChatPanel extends React.Component { render() { - const { document, mode, onStartConversation } = this.props; + const { document, mode } = this.props; const { endpointUrl } = document || { endpointUrl: '' }; return (
{endpointUrl}
- +
); } diff --git a/packages/app/client/src/ui/editor/emulator/emulator.spec.tsx b/packages/app/client/src/ui/editor/emulator/emulator.spec.tsx index e543c0573..51e28599c 100644 --- a/packages/app/client/src/ui/editor/emulator/emulator.spec.tsx +++ b/packages/app/client/src/ui/editor/emulator/emulator.spec.tsx @@ -34,16 +34,16 @@ import * as React from 'react'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; -import { BehaviorSubject, Subscription } from 'rxjs'; import { mount, shallow } from 'enzyme'; -import { SharedConstants } from '@bfemulator/app-shared'; +import { DebugMode, SharedConstants } from '@bfemulator/app-shared'; import base64Url from 'base64url'; import { disable, enable } from '../../../data/action/presentationActions'; -import { clearLog, newConversation, setInspectorObjects, updateChat } from '../../../data/action/chatActions'; +import { clearLog, newConversation, setInspectorObjects } from '../../../data/action/chatActions'; import { updateDocument } from '../../../data/action/editorActions'; -import { Emulator, EmulatorComponent, RestartConversationOptions } from './emulator'; +import { Emulator, RestartConversationOptions } from './emulator'; +import { EmulatorContainer } from './emulatorContainer'; const { encode } = base64Url; @@ -63,6 +63,10 @@ jest.mock('../../../platform/commands/commandServiceImpl', () => ({ if (commandName === mockSharedConstants.Commands.Emulator.FeedTranscriptFromDisk) { return Promise.resolve({ meta: 'some file info' }); } + if (commandName === mockSharedConstants.Commands.Settings.LoadAppSettings) { + return Promise.resolve({ framework: { userGUID: '' } }); + } + return Promise.resolve(); }, }, @@ -92,7 +96,7 @@ jest.mock('botframework-webchat', () => ({ createDirectLine: args => ({ ...args }), })); -describe('', () => { +describe('', () => { let wrapper; let node; let instance; @@ -125,15 +129,16 @@ describe('', () => { }, }, presentation: { enabled: true }, + clientAwareSettings: { debugMode: DebugMode.Normal }, }; const mockStore = createStore((_state, _action) => mockStoreState); mockDispatch = jest.spyOn(mockStore, 'dispatch'); wrapper = mount( - + ); - node = wrapper.find(EmulatorComponent); + node = wrapper.find(Emulator); instance = node.instance(); }); @@ -151,7 +156,7 @@ describe('', () => { it('should render the presentation view', () => { wrapper = shallow( - null)} newConversation={jest.fn(() => null)} mode={'transcript'} @@ -166,7 +171,7 @@ describe('', () => { it('should render the default view', () => { wrapper = shallow( - null)} newConversation={jest.fn(() => null)} mode={'transcript'} @@ -189,7 +194,7 @@ describe('', () => { }, }; wrapper = shallow( - null)} newConversation={jest.fn(() => null)} mode={'transcript'} @@ -211,7 +216,7 @@ describe('', () => { }, }; wrapper = shallow( - null)} newConversation={jest.fn(() => null)} mode={'transcript'} @@ -226,7 +231,7 @@ describe('', () => { it('should restart the conversation on Ctrl/Cmd + Shift + R', () => { wrapper = shallow( - null)} newConversation={jest.fn(() => null)} mode={'transcript'} @@ -283,7 +288,7 @@ describe('', () => { conversationId: 'convo1', }; wrapper = shallow( - null)} newConversation={jest.fn(() => null)} mode={'transcript'} @@ -293,30 +298,30 @@ describe('', () => { instance = wrapper.instance(); instance.onExportClick(); - expect(mockRemoteCallsMade).toHaveLength(2); - expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.SaveTranscriptToFile); - expect(mockRemoteCallsMade[1].args).toEqual(['convo1']); + expect(mockRemoteCallsMade).toHaveLength(1); + expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.SaveTranscriptToFile); + expect(mockRemoteCallsMade[0].args).toEqual(['convo1']); }); it('should start a new conversation', async () => { - const mockInitConversation = jest.fn(() => null); - instance.initConversation = mockInitConversation; + const initConversationSpy = jest.spyOn(instance, 'initConversation'); const options = { - conversationId: 'convo1', - conversationMode: instance.props.mode, - endpointId: instance.props.endpointId, - userId: 'someUserId', + conversationId: 'someUniqueId|livechat', + conversationMode: 'livechat', + endpointId: 'endpoint1', + userId: 'newUserId', }; - await instance.startNewConversation(undefined, false, false); + await instance.startNewConversation(undefined, true, true); - expect(mockUnsubscribe).toHaveBeenCalled(); expect(mockRemoteCallsMade).toHaveLength(1); - expect(mockInitConversation).toHaveBeenCalledWith( - instance.props, - options, - jasmine.any(BehaviorSubject), - jasmine.any(Subscription) - ); + expect(initConversationSpy).toHaveBeenCalledWith(instance.props, options); + }); + + it('should start a new conversation when a new document is given as props', async () => { + const startNewConversationSpy = jest.spyOn(instance, 'startNewConversation'); + const nextProps = { document: { documentId: 'newDoc' } }; + instance.componentWillReceiveProps(nextProps); + expect(startNewConversationSpy).toHaveBeenCalledWith(nextProps); }); it('should start a new conversation with a new conversation id', async () => { @@ -330,12 +335,7 @@ describe('', () => { }; await instance.startNewConversation(undefined, true, false); - expect(mockInitConversation).toHaveBeenCalledWith( - instance.props, - options, - jasmine.any(BehaviorSubject), - jasmine.any(Subscription) - ); + expect(mockInitConversation).toHaveBeenCalledWith(instance.props, options); }); it('should start a new conversation with a new user id', async () => { @@ -350,15 +350,10 @@ describe('', () => { }; await instance.startNewConversation(undefined, false, true); - expect(mockRemoteCallsMade).toHaveLength(2); - expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.SetCurrentUser); - expect(mockRemoteCallsMade[1].args).toEqual([options.userId]); - expect(mockInitConversation).toHaveBeenCalledWith( - instance.props, - options, - jasmine.any(BehaviorSubject), - jasmine.any(Subscription) - ); + expect(mockRemoteCallsMade).toHaveLength(1); + expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.SetCurrentUser); + expect(mockRemoteCallsMade[0].args).toEqual([options.userId]); + expect(mockInitConversation).toHaveBeenCalledWith(instance.props, options); }); it('should start over a conversation with a new user id on click', async () => { @@ -368,9 +363,9 @@ describe('', () => { expect(mockDispatch).toHaveBeenCalledWith(clearLog('doc1')); expect(mockDispatch).toHaveBeenCalledWith(setInspectorObjects('doc1', [])); - expect(mockRemoteCallsMade).toHaveLength(2); - expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent); - expect(mockRemoteCallsMade[1].args).toEqual(['conversation_restart', { userId: 'new' }]); + expect(mockRemoteCallsMade).toHaveLength(1); + expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent); + expect(mockRemoteCallsMade[0].args).toEqual(['conversation_restart', { userId: 'new' }]); expect(mockStartNewConversation).toHaveBeenCalledWith(undefined, true, true); }); @@ -381,9 +376,9 @@ describe('', () => { expect(mockDispatch).toHaveBeenCalledWith(clearLog('doc1')); expect(mockDispatch).toHaveBeenCalledWith(setInspectorObjects('doc1', [])); - expect(mockRemoteCallsMade).toHaveLength(2); - expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent); - expect(mockRemoteCallsMade[1].args).toEqual(['conversation_restart', { userId: 'same' }]); + expect(mockRemoteCallsMade).toHaveLength(1); + expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Telemetry.TrackEvent); + expect(mockRemoteCallsMade[0].args).toEqual(['conversation_restart', { userId: 'same' }]); expect(mockStartNewConversation).toHaveBeenCalledWith(undefined, true, false); }); @@ -404,8 +399,6 @@ describe('', () => { domain: 'someUrl/v3/directline', webSocket: false, }, - selectedActivity$: {}, - subscription: {}, }) ); }); @@ -425,11 +418,11 @@ describe('', () => { await instance.startNewConversation(mockProps); - expect(mockRemoteCallsMade).toHaveLength(3); - expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript); - expect(mockRemoteCallsMade[1].args).toEqual(['someUniqueId|transcript']); - expect(mockRemoteCallsMade[2].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromMemory); - expect(mockRemoteCallsMade[2].args).toEqual(['someConvoId', 'someBotId', 'someUserId', []]); + expect(mockRemoteCallsMade).toHaveLength(2); + expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript); + expect(mockRemoteCallsMade[0].args).toEqual(['someUniqueId|transcript']); + expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromMemory); + expect(mockRemoteCallsMade[1].args).toEqual(['someConvoId', 'someBotId', 'someUserId', []]); }); it('should start a new conversation from transcript on disk', async () => { @@ -449,11 +442,11 @@ describe('', () => { await instance.startNewConversation(mockProps); - expect(mockRemoteCallsMade).toHaveLength(3); - expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript); - expect(mockRemoteCallsMade[1].args).toEqual(['someUniqueId|transcript']); - expect(mockRemoteCallsMade[2].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromDisk); - expect(mockRemoteCallsMade[2].args).toEqual(['someConvoId', 'someBotId', 'someUserId', 'someDocId']); + expect(mockRemoteCallsMade).toHaveLength(2); + expect(mockRemoteCallsMade[0].commandName).toBe(SharedConstants.Commands.Emulator.NewTranscript); + expect(mockRemoteCallsMade[0].args).toEqual(['someUniqueId|transcript']); + expect(mockRemoteCallsMade[1].commandName).toBe(SharedConstants.Commands.Emulator.FeedTranscriptFromDisk); + expect(mockRemoteCallsMade[1].args).toEqual(['someConvoId', 'someBotId', 'someUserId', 'someDocId']); expect(mockDispatch).toHaveBeenCalledWith(updateDocument('someDocId', { meta: 'some file info' })); }); }); diff --git a/packages/app/client/src/ui/editor/emulator/emulator.tsx b/packages/app/client/src/ui/editor/emulator/emulator.tsx index 7b3ace231..5bdedd662 100644 --- a/packages/app/client/src/ui/editor/emulator/emulator.tsx +++ b/packages/app/client/src/ui/editor/emulator/emulator.tsx @@ -32,23 +32,17 @@ // import { createDirectLine } from 'botframework-webchat'; -import { Activity, uniqueId, uniqueIdv4 } from '@bfemulator/sdk-shared'; -import { Splitter, SplitButton } from '@bfemulator/ui-react'; +import { uniqueId, uniqueIdv4 } from '@bfemulator/sdk-shared'; +import { SplitButton, Splitter } from '@bfemulator/ui-react'; import base64Url from 'base64url'; import { IEndpointService } from 'botframework-config/lib/schema'; import * as React from 'react'; -import { connect } from 'react-redux'; -import { BehaviorSubject } from 'rxjs'; import { newNotification, Notification, SharedConstants } from '@bfemulator/app-shared'; +import { DebugMode } from '@bfemulator/app-shared'; -import * as ChatActions from '../../../data/action/chatActions'; -import { updateDocument } from '../../../data/action/editorActions'; -import * as PresentationActions from '../../../data/action/presentationActions'; import { Document } from '../../../data/reducer/editor'; -import { RootState } from '../../../data/store'; import { CommandServiceImpl } from '../../../platform/commands/commandServiceImpl'; import { debounce } from '../../../utils'; -import { beginAdd } from '../../../data/action/notificationActions'; import ChatPanel from './chatPanel/chatPanel'; import LogPanel from './logPanel/logPanel'; @@ -66,11 +60,12 @@ export const RestartConversationOptions = { export type EmulatorMode = 'transcript' | 'livechat'; -interface EmulatorProps { +export interface EmulatorProps { activeDocumentId?: string; clearLog?: (documentId: string) => void; conversationId?: string; createErrorNotification?: (notification: Notification) => void; + debugMode?: DebugMode; dirty?: boolean; document?: any; documentId?: string; @@ -87,7 +82,7 @@ interface EmulatorProps { url?: string; } -export class EmulatorComponent extends React.Component { +export class Emulator extends React.Component { private readonly onVerticalSizeChange = debounce(sizes => { this.props.document.ui = { ...this.props.document.ui, @@ -117,12 +112,6 @@ export class EmulatorComponent extends React.Component { } } - componentDidMount() { - // WebChat v3 vs v4 behavior indie the Emulator has diverted in behavior such that - // no activity gets to the bot under the conversation is restarted. - this.onStartOverClick(RestartConversationOptions.SameUserId); - } - componentWillUnmount() { window.removeEventListener('keydown', this.keyboardEventListener); } @@ -145,7 +134,6 @@ export class EmulatorComponent extends React.Component { if (switchedDocuments) { if (switchedToThisDocument) { - window.removeEventListener('keydown', keyboardEventListener); window.addEventListener('keydown', keyboardEventListener); } else { window.removeEventListener('keydown', keyboardEventListener); @@ -158,16 +146,6 @@ export class EmulatorComponent extends React.Component { requireNewConvoId: boolean = false, requireNewUserId: boolean = false ): Promise => { - if (props.document.subscription) { - props.document.subscription.unsubscribe(); - } - const selectedActivity$ = new BehaviorSubject({}); - const subscription = selectedActivity$.subscribe(activity => { - if (activity && activity.showInInspector) { - this.props.setInspectorObjects(props.document.documentId, activity); - } - }); - // Look for an existing conversation ID and use that, // otherwise, create a new one const conversationId = requireNewConvoId @@ -190,7 +168,7 @@ export class EmulatorComponent extends React.Component { props.document.directLine.end(); } - this.initConversation(props, options, selectedActivity$, subscription); + this.initConversation(props, options); if (props.mode === 'transcript') { try { @@ -240,7 +218,7 @@ export class EmulatorComponent extends React.Component { } }; - initConversation(props: EmulatorProps, options: any, selectedActivity$: any, subscription: any): void { + initConversation(props: EmulatorProps, options: any): void { const encodedOptions = encode(JSON.stringify(options)); // TODO: We need to use encoded token because we need to pass both endpoint ID and conversation ID @@ -255,8 +233,6 @@ export class EmulatorComponent extends React.Component { conversationId: options.conversationId, // webChatStore, directLine, - selectedActivity$, - subscription, userId: options.userId, }); } @@ -275,11 +251,7 @@ export class EmulatorComponent extends React.Component { return (
- + {chatPanelChild}
this.onPresentationClick(false)} /> @@ -299,6 +271,7 @@ export class EmulatorComponent extends React.Component { defaultLabel="Restart conversation" buttonClass={styles.restartIcon} options={[NewUserId, SameUserId]} + disabled={this.props.debugMode === DebugMode.Sidecar} onClick={this.onStartOverClick} />