botframework-webchat 4.13.0 → 4.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/.eslintrc.yml +5 -104
  2. package/.prettierrc.yml +1 -1
  3. package/README.md +33 -36
  4. package/babel.config.json +2 -2
  5. package/babel.sanitize-html.config.json +10 -0
  6. package/lib/AddFullBundle.d.ts +30 -0
  7. package/lib/AddFullBundle.d.ts.map +1 -0
  8. package/lib/AddFullBundle.js +76 -0
  9. package/lib/FullComposer.d.ts +8 -0
  10. package/lib/FullComposer.d.ts.map +1 -0
  11. package/lib/FullComposer.js +6 -16
  12. package/lib/FullReactWebChat.d.ts +8 -0
  13. package/lib/FullReactWebChat.d.ts.map +1 -0
  14. package/lib/FullReactWebChat.js +8 -23
  15. package/lib/adaptiveCards/AdaptiveCardsComposer.d.ts +10 -0
  16. package/lib/adaptiveCards/AdaptiveCardsComposer.d.ts.map +1 -0
  17. package/lib/adaptiveCards/AdaptiveCardsComposer.js +6 -2
  18. package/lib/adaptiveCards/AdaptiveCardsContext.d.ts +8 -0
  19. package/lib/adaptiveCards/AdaptiveCardsContext.d.ts.map +1 -0
  20. package/lib/adaptiveCards/AdaptiveCardsContext.js +3 -3
  21. package/lib/adaptiveCards/AdaptiveCardsStyleOptions.d.ts +5 -3
  22. package/lib/adaptiveCards/AdaptiveCardsStyleOptions.d.ts.map +1 -1
  23. package/lib/adaptiveCards/AdaptiveCardsStyleSet.d.ts +7 -0
  24. package/lib/adaptiveCards/AdaptiveCardsStyleSet.d.ts.map +1 -0
  25. package/lib/{FullBundleStyleOptions.js → adaptiveCards/AdaptiveCardsStyleSet.js} +0 -0
  26. package/lib/adaptiveCards/Attachment/AdaptiveCardAttachment.d.ts +9 -0
  27. package/lib/adaptiveCards/Attachment/AdaptiveCardAttachment.d.ts.map +1 -0
  28. package/lib/adaptiveCards/Attachment/AdaptiveCardAttachment.js +1 -1
  29. package/lib/adaptiveCards/Attachment/AdaptiveCardBuilder.d.ts +28 -0
  30. package/lib/adaptiveCards/Attachment/AdaptiveCardBuilder.d.ts.map +1 -0
  31. package/lib/adaptiveCards/Attachment/AdaptiveCardBuilder.js +13 -6
  32. package/lib/adaptiveCards/Attachment/AdaptiveCardContent.d.ts +9 -0
  33. package/lib/adaptiveCards/Attachment/AdaptiveCardContent.d.ts.map +1 -0
  34. package/lib/adaptiveCards/Attachment/AdaptiveCardContent.js +8 -4
  35. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.d.ts +12 -0
  36. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.d.ts.map +1 -0
  37. package/lib/adaptiveCards/Attachment/AdaptiveCardRenderer.js +310 -85
  38. package/lib/adaptiveCards/Attachment/AnimationCardContent.d.ts +10 -0
  39. package/lib/adaptiveCards/Attachment/AnimationCardContent.d.ts.map +1 -0
  40. package/lib/adaptiveCards/Attachment/AnimationCardContent.js +1 -1
  41. package/lib/adaptiveCards/Attachment/AudioCardContent.d.ts +10 -0
  42. package/lib/adaptiveCards/Attachment/AudioCardContent.d.ts.map +1 -0
  43. package/lib/adaptiveCards/Attachment/AudioCardContent.js +1 -1
  44. package/lib/adaptiveCards/Attachment/CommonCard.js +11 -6
  45. package/lib/adaptiveCards/Attachment/HeroCardContent.d.ts +10 -0
  46. package/lib/adaptiveCards/Attachment/HeroCardContent.d.ts.map +1 -0
  47. package/lib/adaptiveCards/Attachment/HeroCardContent.js +11 -6
  48. package/lib/adaptiveCards/Attachment/OAuthCardContent.d.ts +10 -0
  49. package/lib/adaptiveCards/Attachment/OAuthCardContent.d.ts.map +1 -0
  50. package/lib/adaptiveCards/Attachment/OAuthCardContent.js +11 -6
  51. package/lib/adaptiveCards/Attachment/ReceiptCardContent.d.ts +10 -0
  52. package/lib/adaptiveCards/Attachment/ReceiptCardContent.d.ts.map +1 -0
  53. package/lib/adaptiveCards/Attachment/ReceiptCardContent.js +11 -6
  54. package/lib/adaptiveCards/Attachment/SignInCardContent.d.ts +10 -0
  55. package/lib/adaptiveCards/Attachment/SignInCardContent.d.ts.map +1 -0
  56. package/lib/adaptiveCards/Attachment/SignInCardContent.js +1 -1
  57. package/lib/adaptiveCards/Attachment/ThumbnailCardContent.d.ts +10 -0
  58. package/lib/adaptiveCards/Attachment/ThumbnailCardContent.d.ts.map +1 -0
  59. package/lib/adaptiveCards/Attachment/ThumbnailCardContent.js +11 -6
  60. package/lib/adaptiveCards/Attachment/VideoCardContent.d.ts +20 -0
  61. package/lib/adaptiveCards/Attachment/VideoCardContent.d.ts.map +1 -0
  62. package/lib/adaptiveCards/Attachment/VideoCardContent.js +7 -5
  63. package/lib/adaptiveCards/AttachmentForScreenReader/AdaptiveCardAttachment.js +7 -3
  64. package/lib/adaptiveCards/AttachmentForScreenReader/RichCardAttachment.js +1 -1
  65. package/lib/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.d.ts +59 -0
  66. package/lib/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.d.ts.map +1 -0
  67. package/lib/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.js +8 -1
  68. package/lib/adaptiveCards/Styles/adaptiveCardHostConfig.d.ts +126 -0
  69. package/lib/adaptiveCards/Styles/adaptiveCardHostConfig.d.ts.map +1 -0
  70. package/lib/adaptiveCards/Styles/adaptiveCardHostConfig.js +29 -9
  71. package/lib/adaptiveCards/Styles/createAdaptiveCardsStyleSet.d.ts +5 -0
  72. package/lib/adaptiveCards/Styles/createAdaptiveCardsStyleSet.d.ts.map +1 -0
  73. package/lib/adaptiveCards/Styles/createAdaptiveCardsStyleSet.js +2 -2
  74. package/lib/adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware.d.ts +3 -0
  75. package/lib/adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware.d.ts.map +1 -0
  76. package/lib/adaptiveCards/createAdaptiveCardsAttachmentForScreenReaderMiddleware.js +5 -11
  77. package/lib/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.d.ts +3 -0
  78. package/lib/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.d.ts.map +1 -0
  79. package/lib/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.js +2 -15
  80. package/lib/adaptiveCards/defaultStyleOptions.d.ts +4 -0
  81. package/lib/adaptiveCards/defaultStyleOptions.d.ts.map +1 -0
  82. package/lib/adaptiveCards/defaultStyleOptions.js +3 -2
  83. package/lib/adaptiveCards/hooks/internal/useAdaptiveCardsContext.d.ts +3 -0
  84. package/lib/adaptiveCards/hooks/internal/useAdaptiveCardsContext.d.ts.map +1 -0
  85. package/lib/adaptiveCards/hooks/internal/useAdaptiveCardsContext.js +1 -1
  86. package/lib/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.d.ts +2 -0
  87. package/lib/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.d.ts.map +1 -0
  88. package/lib/adaptiveCards/hooks/internal/useParseAdaptiveCardJSON.js +7 -5
  89. package/lib/adaptiveCards/hooks/internal/useUniqueId.js +1 -1
  90. package/lib/adaptiveCards/hooks/useAdaptiveCardsHostConfig.d.ts +2 -0
  91. package/lib/adaptiveCards/hooks/useAdaptiveCardsHostConfig.d.ts.map +1 -0
  92. package/lib/adaptiveCards/hooks/useAdaptiveCardsHostConfig.js +3 -5
  93. package/lib/adaptiveCards/hooks/useAdaptiveCardsPackage.d.ts +3 -0
  94. package/lib/adaptiveCards/hooks/useAdaptiveCardsPackage.d.ts.map +1 -0
  95. package/lib/adaptiveCards/hooks/useAdaptiveCardsPackage.js +1 -1
  96. package/lib/adaptiveCards/normalizeStyleOptions.d.ts +3 -0
  97. package/lib/adaptiveCards/normalizeStyleOptions.d.ts.map +1 -0
  98. package/lib/adaptiveCards/normalizeStyleOptions.js +1 -1
  99. package/lib/addVersion.js +2 -2
  100. package/lib/createBrowserWebSpeechPonyfillFactory.d.ts +3 -0
  101. package/lib/createBrowserWebSpeechPonyfillFactory.d.ts.map +1 -0
  102. package/lib/createBrowserWebSpeechPonyfillFactory.js +7 -4
  103. package/lib/createCognitiveServicesSpeechServicesPonyfillFactory.d.ts +17 -0
  104. package/lib/createCognitiveServicesSpeechServicesPonyfillFactory.d.ts.map +1 -0
  105. package/lib/createCognitiveServicesSpeechServicesPonyfillFactory.js +17 -11
  106. package/lib/createDirectLine.d.ts +17 -0
  107. package/lib/createDirectLine.d.ts.map +1 -0
  108. package/lib/createDirectLine.js +3 -2
  109. package/lib/createDirectLineAppServiceExtension.d.ts +7 -0
  110. package/lib/createDirectLineAppServiceExtension.d.ts.map +1 -0
  111. package/lib/createDirectLineAppServiceExtension.js +1 -1
  112. package/lib/createDirectLineSpeechAdapters.d.ts +25 -0
  113. package/lib/createDirectLineSpeechAdapters.d.ts.map +1 -0
  114. package/lib/createDirectLineSpeechAdapters.js +57 -3
  115. package/lib/createFullStyleSet.d.ts +1375 -0
  116. package/lib/createFullStyleSet.d.ts.map +1 -0
  117. package/lib/createFullStyleSet.js +2 -2
  118. package/lib/fullBundleDefaultStyleOptions.js +4 -4
  119. package/lib/hooks/useStyleOptions.d.ts +3 -0
  120. package/lib/hooks/useStyleOptions.d.ts.map +1 -0
  121. package/lib/hooks/useStyleOptions.js +21 -0
  122. package/lib/index-es5.d.ts +4 -20
  123. package/lib/index-es5.d.ts.map +1 -1
  124. package/lib/index-es5.js +3 -43
  125. package/lib/index-minimal.d.ts +9 -6
  126. package/lib/index-minimal.d.ts.map +1 -1
  127. package/lib/index-minimal.js +27 -21
  128. package/lib/index.d.ts +164 -18
  129. package/lib/index.d.ts.map +1 -1
  130. package/lib/index.js +19 -12
  131. package/lib/polyfill.d.ts +23 -0
  132. package/lib/polyfill.d.ts.map +1 -0
  133. package/lib/polyfill.js +46 -0
  134. package/lib/renderMarkdown.d.ts +6 -0
  135. package/lib/renderMarkdown.d.ts.map +1 -0
  136. package/lib/renderMarkdown.js +36 -5
  137. package/lib/renderWebChat.d.ts +3 -0
  138. package/lib/renderWebChat.d.ts.map +1 -0
  139. package/lib/renderWebChat.js +1 -1
  140. package/lib/speech/CustomAudioInputStream.d.ts +94 -0
  141. package/lib/speech/CustomAudioInputStream.d.ts.map +1 -0
  142. package/lib/speech/CustomAudioInputStream.js +441 -0
  143. package/lib/speech/bytesPerSample.d.ts +2 -0
  144. package/lib/speech/bytesPerSample.d.ts.map +1 -0
  145. package/lib/speech/bytesPerSample.js +12 -0
  146. package/lib/speech/createAudioConfig.d.ts +23 -0
  147. package/lib/speech/createAudioConfig.d.ts.map +1 -0
  148. package/lib/speech/createAudioConfig.js +90 -0
  149. package/lib/speech/createAudioContext.d.ts +3 -0
  150. package/lib/speech/createAudioContext.d.ts.map +1 -0
  151. package/lib/speech/createAudioContext.js +22 -0
  152. package/lib/speech/createMicrophoneAudioConfigAndAudioContext.d.ts +9 -0
  153. package/lib/speech/createMicrophoneAudioConfigAndAudioContext.d.ts.map +1 -0
  154. package/lib/speech/createMicrophoneAudioConfigAndAudioContext.js +161 -0
  155. package/lib/speech/getUserMedia.d.ts +2 -0
  156. package/lib/speech/getUserMedia.d.ts.map +1 -0
  157. package/lib/speech/getUserMedia.js +25 -0
  158. package/lib/types/AdaptiveCardsPackage.d.ts +13 -0
  159. package/lib/types/AdaptiveCardsPackage.d.ts.map +1 -0
  160. package/lib/types/AdaptiveCardsPackage.js +2 -0
  161. package/lib/types/CognitiveServicesAudioOutputFormat.d.ts +3 -0
  162. package/lib/types/CognitiveServicesAudioOutputFormat.d.ts.map +1 -0
  163. package/lib/types/CognitiveServicesAudioOutputFormat.js +2 -0
  164. package/lib/types/CognitiveServicesCredentials.d.ts +18 -0
  165. package/lib/types/CognitiveServicesCredentials.d.ts.map +1 -0
  166. package/lib/types/CognitiveServicesCredentials.js +2 -0
  167. package/lib/types/CognitiveServicesTextNormalization.d.ts +3 -0
  168. package/lib/types/CognitiveServicesTextNormalization.d.ts.map +1 -0
  169. package/lib/types/CognitiveServicesTextNormalization.js +2 -0
  170. package/lib/{FullBundleStyleOptions.d.ts → types/FullBundleStyleOptions.d.ts} +1 -1
  171. package/lib/types/FullBundleStyleOptions.d.ts.map +1 -0
  172. package/lib/types/FullBundleStyleOptions.js +2 -0
  173. package/lib/useComposerProps.d.ts +22 -0
  174. package/lib/useComposerProps.d.ts.map +1 -0
  175. package/lib/useComposerProps.js +6 -8
  176. package/package.json +46 -48
  177. package/src/AddFullBundle.tsx +93 -0
  178. package/src/FullComposer.tsx +38 -0
  179. package/src/FullReactWebChat.tsx +32 -0
  180. package/src/__tests__/createDirectLine.spec.js +2 -0
  181. package/src/__tests__/renderMarkdown.spec.js +1 -1
  182. package/src/__tests__/versionTag.es5.spec.js +3 -0
  183. package/src/__tests__/versionTag.full.spec.js +3 -0
  184. package/src/__tests__/versionTag.minimal.spec.js +3 -0
  185. package/src/adaptiveCards/{AdaptiveCardsComposer.js → AdaptiveCardsComposer.tsx} +17 -5
  186. package/src/adaptiveCards/AdaptiveCardsContext.ts +12 -0
  187. package/src/adaptiveCards/AdaptiveCardsStyleOptions.ts +5 -3
  188. package/src/adaptiveCards/AdaptiveCardsStyleSet.ts +7 -0
  189. package/src/adaptiveCards/Attachment/{AdaptiveCardAttachment.js → AdaptiveCardAttachment.tsx} +8 -2
  190. package/src/adaptiveCards/Attachment/AdaptiveCardBuilder.ts +27 -17
  191. package/src/adaptiveCards/Attachment/{AdaptiveCardContent.js → AdaptiveCardContent.tsx} +8 -2
  192. package/src/adaptiveCards/Attachment/AdaptiveCardRenderer.tsx +768 -0
  193. package/src/adaptiveCards/Attachment/{AnimationCardContent.js → AnimationCardContent.tsx} +9 -2
  194. package/src/adaptiveCards/Attachment/{AudioCardContent.js → AudioCardContent.tsx} +9 -2
  195. package/src/adaptiveCards/Attachment/CommonCard.js +2 -1
  196. package/src/adaptiveCards/Attachment/{HeroCardContent.js → HeroCardContent.tsx} +12 -3
  197. package/src/adaptiveCards/Attachment/{OAuthCardContent.js → OAuthCardContent.tsx} +11 -3
  198. package/src/adaptiveCards/Attachment/{ReceiptCardContent.js → ReceiptCardContent.tsx} +58 -28
  199. package/src/adaptiveCards/Attachment/{SignInCardContent.js → SignInCardContent.tsx} +9 -2
  200. package/src/adaptiveCards/Attachment/{ThumbnailCardContent.js → ThumbnailCardContent.tsx} +12 -3
  201. package/src/adaptiveCards/Attachment/{VideoCardContent.js → VideoCardContent.tsx} +15 -3
  202. package/src/adaptiveCards/Styles/StyleSet/AdaptiveCardRenderer.ts +9 -1
  203. package/src/adaptiveCards/Styles/adaptiveCardHostConfig.ts +28 -8
  204. package/src/adaptiveCards/Styles/createAdaptiveCardsStyleSet.spec.js +2 -0
  205. package/src/adaptiveCards/Styles/createAdaptiveCardsStyleSet.ts +7 -4
  206. package/src/adaptiveCards/{createAdaptiveCardsAttachmentForScreenReaderMiddleware.js → createAdaptiveCardsAttachmentForScreenReaderMiddleware.tsx} +16 -15
  207. package/src/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.tsx +44 -0
  208. package/src/adaptiveCards/defaultStyleOptions.ts +2 -1
  209. package/src/adaptiveCards/hooks/internal/{useAdaptiveCardsContext.js → useAdaptiveCardsContext.ts} +1 -1
  210. package/src/adaptiveCards/hooks/internal/{useParseAdaptiveCardJSON.js → useParseAdaptiveCardJSON.ts} +5 -2
  211. package/src/adaptiveCards/hooks/internal/{useUniqueId.js → useUniqueId.ts} +1 -1
  212. package/src/adaptiveCards/hooks/{useAdaptiveCardsHostConfig.js → useAdaptiveCardsHostConfig.ts} +6 -8
  213. package/src/adaptiveCards/hooks/{useAdaptiveCardsPackage.js → useAdaptiveCardsPackage.ts} +2 -1
  214. package/src/createBrowserWebSpeechPonyfillFactory.ts +21 -0
  215. package/src/createCognitiveServicesSpeechServicesPonyfillFactory.spec.js +36 -6
  216. package/src/createCognitiveServicesSpeechServicesPonyfillFactory.ts +78 -0
  217. package/src/createDirectLine.ts +65 -0
  218. package/src/createDirectLineAppServiceExtension.ts +22 -0
  219. package/src/createDirectLineSpeechAdapters.ts +84 -0
  220. package/src/createFullStyleSet.ts +2 -2
  221. package/src/fullBundleDefaultStyleOptions.ts +3 -3
  222. package/src/hooks/useStyleOptions.ts +9 -0
  223. package/src/index-es5.ts +3 -28
  224. package/src/index-minimal.ts +3 -2
  225. package/src/index.ts +22 -10
  226. package/src/polyfill.ts +29 -0
  227. package/src/renderMarkdown.ts +140 -0
  228. package/src/renderWebChat.tsx +6 -0
  229. package/src/speech/CustomAudioInputStream.ts +356 -0
  230. package/src/speech/bytesPerSample.ts +4 -0
  231. package/src/speech/createAudioConfig.spec.js +23 -0
  232. package/src/speech/createAudioConfig.ts +77 -0
  233. package/src/speech/createAudioContext.ts +16 -0
  234. package/src/speech/createMicrophoneAudioConfigAndAudioContext.ts +146 -0
  235. package/src/speech/getUserMedia.ts +14 -0
  236. package/src/tsconfig.json +1 -1
  237. package/src/types/AdaptiveCardsPackage.ts +23 -0
  238. package/src/types/CognitiveServicesAudioOutputFormat.ts +28 -0
  239. package/src/types/CognitiveServicesCredentials.ts +28 -0
  240. package/src/types/CognitiveServicesTextNormalization.ts +3 -0
  241. package/src/{FullBundleStyleOptions.ts → types/FullBundleStyleOptions.ts} +1 -1
  242. package/src/{useComposerProps.js → useComposerProps.ts} +21 -11
  243. package/webpack.config.js +46 -6
  244. package/.eslintignore +0 -9
  245. package/lib/FullBundleStyleOptions.d.ts.map +0 -1
  246. package/src/FullComposer.js +0 -40
  247. package/src/FullReactWebChat.js +0 -47
  248. package/src/adaptiveCards/AdaptiveCardsContext.js +0 -5
  249. package/src/adaptiveCards/Attachment/AdaptiveCardRenderer.js +0 -492
  250. package/src/adaptiveCards/createAdaptiveCardsAttachmentMiddleware.js +0 -41
  251. package/src/createBrowserWebSpeechPonyfillFactory.js +0 -16
  252. package/src/createCognitiveServicesSpeechServicesPonyfillFactory.js +0 -58
  253. package/src/createDirectLine.js +0 -38
  254. package/src/createDirectLineAppServiceExtension.js +0 -12
  255. package/src/createDirectLineSpeechAdapters.js +0 -5
  256. package/src/index-es5.tsx +0 -49
  257. package/src/index-minimal.tsx +0 -9
  258. package/src/index.tsx +0 -45
  259. package/src/renderMarkdown.js +0 -97
  260. package/src/renderWebChat.js +0 -6
@@ -0,0 +1,140 @@
1
+ /* eslint no-magic-numbers: ["error", { "ignore": [0, 1, 2] }] */
2
+
3
+ import iterator from 'markdown-it-for-inline';
4
+ import MarkdownIt from 'markdown-it';
5
+ import markdownItAttrs from 'markdown-it-attrs-es5';
6
+ import sanitizeHTML from 'sanitize-html';
7
+
8
+ const SANITIZE_HTML_OPTIONS = {
9
+ allowedAttributes: {
10
+ a: ['aria-label', 'href', 'name', 'rel', 'target', 'title'],
11
+ img: ['alt', 'class', 'src']
12
+ },
13
+ allowedSchemes: ['data', 'http', 'https', 'ftp', 'mailto', 'sip', 'tel'],
14
+ allowedTags: [
15
+ 'a',
16
+ 'b',
17
+ 'blockquote',
18
+ 'br',
19
+ 'caption',
20
+ 'code',
21
+ 'del',
22
+ 'div',
23
+ 'em',
24
+ 'h1',
25
+ 'h2',
26
+ 'h3',
27
+ 'h4',
28
+ 'h5',
29
+ 'h6',
30
+ 'hr',
31
+ 'i',
32
+ 'img',
33
+ 'ins',
34
+ 'li',
35
+ 'nl',
36
+ 'ol',
37
+ 'p',
38
+ 'pre',
39
+ 's',
40
+ 'span',
41
+ 'strike',
42
+ 'strong',
43
+ 'table',
44
+ 'tbody',
45
+ 'td',
46
+ 'tfoot',
47
+ 'th',
48
+ 'thead',
49
+ 'tr',
50
+ 'ul'
51
+ ]
52
+ };
53
+
54
+ // Put a transparent pixel instead of the "open in new window" icon, so developers can easily modify the icon in CSS.
55
+ const TRANSPARENT_GIF = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
56
+
57
+ // This is used for parsing Markdown for external links.
58
+ const internalMarkdownIt = new MarkdownIt();
59
+
60
+ const MARKDOWN_ATTRS_LEFT_DELIMITER = '⟬';
61
+ // Make sure the delimiter is free from any RegExp characters, such as *, ?, etc.
62
+ // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
63
+ // eslint-disable-next-line security/detect-non-literal-regexp, require-unicode-regexp
64
+ const MARKDOWN_ATTRS_LEFT_DELIMITER_PATTERN = new RegExp(MARKDOWN_ATTRS_LEFT_DELIMITER, 'g');
65
+
66
+ const MARKDOWN_ATTRS_RIGHT_DELIMITER = '⟭';
67
+ // Make sure the delimiter is free from any RegExp characters, such as *, ?, etc.
68
+ // IE11 does not support "u" flag and Babel could not remove it. We intentionally omitting the "u" flag here.
69
+ // eslint-disable-next-line security/detect-non-literal-regexp, require-unicode-regexp
70
+ const MARKDOWN_ATTRS_RIGHT_DELIMITER_PATTERN = new RegExp(MARKDOWN_ATTRS_RIGHT_DELIMITER, 'g');
71
+
72
+ export default function render(
73
+ markdown: string,
74
+ { markdownRespectCRLF }: { markdownRespectCRLF: boolean },
75
+ { externalLinkAlt = '' }: { externalLinkAlt?: string } = {}
76
+ ): string {
77
+ if (markdownRespectCRLF) {
78
+ markdown = markdown.replace(/\n\r|\r\n/gu, carriageReturn => (carriageReturn === '\n\r' ? '\r\n' : '\n\r'));
79
+ }
80
+
81
+ // Related to #3165.
82
+ // We only support attributes "aria-label" and should leave other attributes as-is.
83
+ // However, `markdown-it-attrs` remove unrecognized attributes, such as {hello}.
84
+ // Before passing to `markdown-it-attrs`, we will convert known attributes from {aria-label="..."} into ⟬aria-label="..."⟭ (using white tortoise shell brackets).
85
+ // Then, we ask `markdown-it-attrs` to only process the new brackets, so it should only try to process things that we allowlisted.
86
+ // Lastly, we revert tortoise shell brackets back to curly brackets, for unprocessed attributes.
87
+ markdown = markdown
88
+ .replace(/\{\s*aria-label()\s*\}/giu, `${MARKDOWN_ATTRS_LEFT_DELIMITER}aria-label${MARKDOWN_ATTRS_RIGHT_DELIMITER}`)
89
+ .replace(
90
+ /\{\s*aria-label=("[^"]*"|[^\s}]*)\s*\}/giu,
91
+ (_, valueInsideQuotes) =>
92
+ `${MARKDOWN_ATTRS_LEFT_DELIMITER}aria-label=${valueInsideQuotes}${MARKDOWN_ATTRS_RIGHT_DELIMITER}`
93
+ );
94
+
95
+ let html = new MarkdownIt({
96
+ breaks: false,
97
+ html: false,
98
+ linkify: true,
99
+ typographer: true,
100
+ xhtmlOut: true
101
+ })
102
+ .use(markdownItAttrs, {
103
+ // `markdown-it-attrs` is added for accessibility and allow bot developers to specify `aria-label`.
104
+ // We are allowlisting `aria-label` only as it is allowlisted in `sanitize-html`.
105
+ // Other `aria-*` will be sanitized even we allowlisted here.
106
+ allowedAttributes: ['aria-label'],
107
+ leftDelimiter: MARKDOWN_ATTRS_LEFT_DELIMITER,
108
+ rightDelimiter: MARKDOWN_ATTRS_RIGHT_DELIMITER
109
+ })
110
+ .use(iterator, 'url_new_win', 'link_open', (tokens, index) => {
111
+ const token = tokens[+index];
112
+
113
+ token.attrSet('rel', 'noopener noreferrer');
114
+ token.attrSet('target', '_blank');
115
+
116
+ const linkOpenToken = tokens.find(({ type }) => type === 'link_open');
117
+ const [, href] = linkOpenToken.attrs.find(([name]) => name === 'href');
118
+
119
+ // Adds a new icon if the link is http: or https:.
120
+ // Don't add if it's a phone number, etc.
121
+ if (/^https?:/iu.test(href)) {
122
+ externalLinkAlt && token.attrSet('title', externalLinkAlt);
123
+
124
+ const iconTokens = internalMarkdownIt.parseInline(`![${externalLinkAlt}](${TRANSPARENT_GIF})`)[0].children;
125
+
126
+ iconTokens[0].attrJoin('class', 'webchat__markdown__external-link-icon');
127
+
128
+ tokens.splice(index + 2, 0, ...iconTokens);
129
+ }
130
+ })
131
+ .render(markdown);
132
+
133
+ // Restore attributes not processed by `markdown-it-attrs`.
134
+ // TODO: [P2] #2511 After we fixed our polyfill story, we should use "String.prototype.replaceAll" instead of RegExp for replace all occurrences.
135
+ html = html.replace(MARKDOWN_ATTRS_LEFT_DELIMITER_PATTERN, '{').replace(MARKDOWN_ATTRS_RIGHT_DELIMITER_PATTERN, '}');
136
+
137
+ // The signature from "sanitize-html" module is not correct.
138
+ // @ts-ignore
139
+ return sanitizeHTML(html, SANITIZE_HTML_OPTIONS);
140
+ }
@@ -0,0 +1,6 @@
1
+ import React, { ComponentType } from 'react';
2
+ import ReactDOM from 'react-dom';
3
+
4
+ export default function renderWebChat(ReactWebChat: ComponentType<any>, props: any, element: HTMLElement): void {
5
+ ReactDOM.render(<ReactWebChat {...props} />, element);
6
+ }
@@ -0,0 +1,356 @@
1
+ import { AudioInputStream } from 'microsoft-cognitiveservices-speech-sdk';
2
+
3
+ import {
4
+ AudioSourceErrorEvent,
5
+ AudioSourceEvent,
6
+ AudioSourceInitializingEvent,
7
+ AudioSourceOffEvent,
8
+ AudioSourceReadyEvent,
9
+ AudioStreamNodeAttachedEvent,
10
+ AudioStreamNodeAttachingEvent,
11
+ AudioStreamNodeDetachedEvent,
12
+ AudioStreamNodeErrorEvent,
13
+ Events,
14
+ EventSource
15
+ } from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/Exports';
16
+
17
+ import { AudioStreamFormatImpl } from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioStreamFormat';
18
+
19
+ import {
20
+ connectivity as Connectivity,
21
+ ISpeechConfigAudioDevice,
22
+ type as Type
23
+ } from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common.speech/Exports';
24
+
25
+ import { isForbiddenPropertyName } from 'botframework-webchat-core';
26
+ import { v4 } from 'uuid';
27
+ import createDeferred, { DeferredPromise } from 'p-defer-es5';
28
+
29
+ type AudioStreamNode = {
30
+ detach: () => Promise<void>;
31
+ id: () => string;
32
+ read: () => Promise<StreamChunk<ArrayBuffer>>;
33
+ };
34
+
35
+ type DeviceInfo = {
36
+ connectivity?: Connectivity | 'Bluetooth' | 'Wired' | 'WiFi' | 'Cellular' | 'InBuilt' | 'Unknown';
37
+ manufacturer?: string;
38
+ model?: string;
39
+ type?:
40
+ | Type
41
+ | 'Phone'
42
+ | 'Speaker'
43
+ | 'Car'
44
+ | 'Headset'
45
+ | 'Thermostat'
46
+ | 'Microphones'
47
+ | 'Deskphone'
48
+ | 'RemoteControl'
49
+ | 'Unknown'
50
+ | 'File'
51
+ | 'Stream';
52
+ };
53
+
54
+ type Format = {
55
+ bitsPerSample: number;
56
+ channels: number;
57
+ samplesPerSec: number;
58
+ };
59
+
60
+ type NormalizedOptions = Required<Omit<Options, 'debug'>> & {
61
+ debug: boolean;
62
+ };
63
+
64
+ type Options = {
65
+ debug?: true;
66
+ id?: string;
67
+ };
68
+
69
+ type StreamChunk<T> = {
70
+ isEnd: boolean;
71
+ buffer: T;
72
+ timeReceived: number;
73
+ };
74
+
75
+ const SYMBOL_DEVICE_INFO_DEFERRED = Symbol('deviceInfoDeferred');
76
+ const SYMBOL_EVENTS = Symbol('events');
77
+ const SYMBOL_FORMAT_DEFERRED = Symbol('formatDeferred');
78
+ const SYMBOL_OPTIONS = Symbol('options');
79
+
80
+ // Speech SDK quirks: Only 2 lifecycle functions are actually used.
81
+ // They are: attach() and turnOff().
82
+ // Others are not used, including: blob(), close(), detach(), turnOn().
83
+ abstract class CustomAudioInputStream extends AudioInputStream {
84
+ constructor(options: Options = {}) {
85
+ super();
86
+
87
+ const normalizedOptions: NormalizedOptions = {
88
+ debug: options.debug || false,
89
+ id: options.id || v4().replace(/-/gu, '')
90
+ };
91
+
92
+ // False alarm: indexer is a constant of type Symbol.
93
+ // eslint-disable-next-line security/detect-object-injection
94
+ this[SYMBOL_DEVICE_INFO_DEFERRED] = createDeferred<DeviceInfo>();
95
+
96
+ // False alarm: indexer is a constant of type Symbol.
97
+ // eslint-disable-next-line security/detect-object-injection
98
+ this[SYMBOL_EVENTS] = new EventSource<AudioSourceEvent>();
99
+
100
+ // False alarm: indexer is a constant of type Symbol.
101
+ // eslint-disable-next-line security/detect-object-injection
102
+ this[SYMBOL_FORMAT_DEFERRED] = createDeferred<AudioStreamFormatImpl>();
103
+
104
+ // False alarm: indexer is a constant of type Symbol.
105
+ // eslint-disable-next-line security/detect-object-injection
106
+ this[SYMBOL_OPTIONS] = normalizedOptions;
107
+ }
108
+
109
+ [SYMBOL_DEVICE_INFO_DEFERRED]: DeferredPromise<DeviceInfo>;
110
+ [SYMBOL_EVENTS]: EventSource<AudioSourceEvent>;
111
+ [SYMBOL_FORMAT_DEFERRED]: DeferredPromise<AudioStreamFormatImpl>;
112
+ [SYMBOL_OPTIONS]: NormalizedOptions;
113
+
114
+ /** Gets the event source for listening to events. */
115
+ // ESLint: This code will only works in browsers other than IE11. Only works in ES5 is okay.
116
+ // @ts-ignore Accessors are only available when targeting ECMAScript 5 and higher.ts(1056)
117
+ get events(): EventSource<AudioSourceEvent> {
118
+ // False alarm: indexer is a constant of type Symbol.
119
+ // eslint-disable-next-line security/detect-object-injection
120
+ return this[SYMBOL_EVENTS];
121
+ }
122
+
123
+ /** Gets the format of the audio stream. */
124
+ // Speech SDK quirks: `AudioStreamFormatImpl` is internal implementation while `AudioStreamFormat` is public.
125
+ // It is weird to expose `AudioStreamFormatImpl` instead of `AudioStreamFormat`.
126
+ // Speech SDK quirks: It is weird to return a `Promise` in a property.
127
+ // Speech SDK quirks: In normal speech recognition, getter of "format" is called only after "attach".
128
+ // But in Direct Line Speech, it is called before "attach".
129
+ // ESLint: This code will only works in browsers other than IE11. Only works in ES5 is okay.
130
+ // @ts-ignore Accessors are only available when targeting ECMAScript 5 and higher.ts(1056)
131
+ get format(): Promise<AudioStreamFormatImpl> {
132
+ this.debug('Getting "format".');
133
+
134
+ // False alarm: indexer is a constant of type Symbol.
135
+ // eslint-disable-next-line security/detect-object-injection
136
+ return this[SYMBOL_FORMAT_DEFERRED].promise;
137
+ }
138
+
139
+ /** Gets the ID of this audio stream. */
140
+ id(): string {
141
+ // False alarm: indexer is a constant of type Symbol.
142
+ // eslint-disable-next-line security/detect-object-injection
143
+ return this[SYMBOL_OPTIONS].id;
144
+ }
145
+
146
+ /** Emits an event. */
147
+ // Speech SDK quirks: In JavaScript, onXxx means "listen to event XXX".
148
+ // Instead, in Speech SDK, it means "emit event XXX".
149
+ protected onEvent(event: AudioSourceEvent): void {
150
+ // False alarm: indexer is a constant of type Symbol.
151
+ // eslint-disable-next-line security/detect-object-injection
152
+ this[SYMBOL_EVENTS].onEvent(event);
153
+ Events.instance.onEvent(event);
154
+ }
155
+
156
+ /** Emits an `AudioSourceInitializingEvent`. */
157
+ protected emitInitializing(): void {
158
+ this.debug('Emitting "AudioSourceInitializingEvent".');
159
+ this.onEvent(new AudioSourceInitializingEvent(this.id()));
160
+ }
161
+
162
+ /** Emits an `AudioSourceReadyEvent`. */
163
+ protected emitReady(): void {
164
+ this.debug('Emitting "AudioSourceReadyEvent".');
165
+ this.onEvent(new AudioSourceReadyEvent(this.id()));
166
+ }
167
+
168
+ /** Emits an `AudioSourceErrorEvent`. */
169
+ // Speech SDK quirks: Since "turnOn" is never called and "turnOff" does not work in Direct Line Speech, the "source error" event is not emitted at all.
170
+ // Instead, we only emit "node error" event.
171
+ protected emitError(error: Error): void {
172
+ this.debug('Emitting "AudioSourceErrorEvent".', { error });
173
+
174
+ // Speech SDK quirks: "error" is a string, instead of object of type "Error".
175
+ this.onEvent(new AudioSourceErrorEvent(this.id(), error.message));
176
+ }
177
+
178
+ /** Emits an `AudioStreamNodeAttachingEvent`. */
179
+ protected emitNodeAttaching(audioNodeId: string): void {
180
+ this.debug(`Emitting "AudioStreamNodeAttachingEvent" for node "${audioNodeId}".`);
181
+ this.onEvent(new AudioStreamNodeAttachingEvent(this.id(), audioNodeId));
182
+ }
183
+
184
+ /** Emits an `AudioStreamNodeAttachedEvent`. */
185
+ protected emitNodeAttached(audioNodeId: string): void {
186
+ this.debug(`Emitting "AudioStreamNodeAttachedEvent" for node "${audioNodeId}".`);
187
+ this.onEvent(new AudioStreamNodeAttachedEvent(this.id(), audioNodeId));
188
+ }
189
+
190
+ /** Emits an `AudioStreamNodeErrorEvent`. */
191
+ protected emitNodeError(audioNodeId: string, error: Error): void {
192
+ this.debug(`Emitting "AudioStreamNodeErrorEvent" for node "${audioNodeId}".`, { error });
193
+
194
+ // Speech SDK quirks: "error" is a string, instead of object of type "Error".
195
+ this.onEvent(new AudioStreamNodeErrorEvent(this.id(), audioNodeId, error.message));
196
+ }
197
+
198
+ /** Emits an `AudioStreamNodeDetachedEvent`. */
199
+ protected emitNodeDetached(audioNodeId: string): void {
200
+ this.debug('Emitting "AudioStreamNodeDetachedEvent".');
201
+ this.onEvent(new AudioStreamNodeDetachedEvent(this.id(), audioNodeId));
202
+ }
203
+
204
+ /** Emits an `AudioSourceOffEvent`. */
205
+ protected emitOff(): void {
206
+ this.debug('Emitting "AudioSourceOffEvent".');
207
+ this.onEvent(new AudioSourceOffEvent(this.id()));
208
+ }
209
+
210
+ // Speech SDK quirks: Although "close" is marked as abstract, it is never called in our observations.
211
+ // ESLint: Speech SDK requires this function, but we are not implementing it.
212
+ close(): void {
213
+ this.debug('Callback for "close".');
214
+
215
+ throw new Error('Not implemented');
216
+ }
217
+
218
+ // Speech SDK quirks: Although "turnOn" is implemented in Speech SDK Push/PullAudioInputStream, it is never called in our observations.
219
+ turnOn(): void {
220
+ this.debug('Callback for "turnOn".');
221
+
222
+ throw new Error('Not implemented');
223
+ }
224
+
225
+ // Speech SDK quirks: Although "detach" is implemented in Speech SDK Push/PullAudioInputStream, it is never called in our observations.
226
+ detach(): void {
227
+ this.debug('Callback for "detach".');
228
+
229
+ throw new Error('Not implemented');
230
+ }
231
+
232
+ /** Log the message to console if `debug` is set to `true`. */
233
+ private debug(message, ...args) {
234
+ // ESLint: For debugging, will only log when "debug" is set to "true".
235
+ // False alarm: indexer is a constant of type Symbol.
236
+ // eslint-disable-next-line no-console, security/detect-object-injection
237
+ this[SYMBOL_OPTIONS].debug && console.info(`CustomAudioInputStream: ${message}`, ...args);
238
+ }
239
+
240
+ /** Implements this function. When called, it should start recording and return an `IAudioStreamNode`. */
241
+ protected abstract performAttach(audioNodeId: string): Promise<{
242
+ audioStreamNode: AudioStreamNode;
243
+ deviceInfo: DeviceInfo;
244
+ format: Format;
245
+ }>;
246
+
247
+ /** Attaches the device by returning an audio node. */
248
+ attach(audioNodeId: string): Promise<AudioStreamNode> {
249
+ this.debug(`Callback for "attach" with "${audioNodeId}".`);
250
+
251
+ this.emitNodeAttaching(audioNodeId);
252
+
253
+ return Promise.resolve().then<AudioStreamNode>(async () => {
254
+ this.emitInitializing();
255
+
256
+ try {
257
+ const { audioStreamNode, deviceInfo, format } = await this.performAttach(audioNodeId);
258
+
259
+ // Although only getter of "format" is called before "attach" (in Direct Line Speech),
260
+ // we are handling both "deviceInfo" and "format" in similar way for uniformity.
261
+
262
+ // False alarm: indexer is a constant of type Symbol.
263
+ // eslint-disable-next-line security/detect-object-injection
264
+ this[SYMBOL_DEVICE_INFO_DEFERRED].resolve(deviceInfo);
265
+
266
+ // False alarm: indexer is a constant of type Symbol.
267
+ // eslint-disable-next-line security/detect-object-injection
268
+ this[SYMBOL_FORMAT_DEFERRED].resolve(
269
+ new AudioStreamFormatImpl(format.samplesPerSec, format.bitsPerSample, format.channels)
270
+ );
271
+
272
+ this.emitReady();
273
+ this.emitNodeAttached(audioNodeId);
274
+
275
+ return {
276
+ detach: async () => {
277
+ this.debug(`Detaching audio node "${audioNodeId}".`);
278
+
279
+ await audioStreamNode.detach();
280
+
281
+ // Speech SDK quirks: Since "turnOff" is not called in Direct Line Speech, we will emit event "source off" here instead.
282
+ this.emitOff();
283
+ this.emitNodeDetached(audioNodeId);
284
+ },
285
+ id: () => audioStreamNode.id(),
286
+ read: () => {
287
+ this.debug('Reading');
288
+
289
+ return audioStreamNode.read();
290
+ }
291
+ };
292
+ } catch (error) {
293
+ this.emitNodeError(audioNodeId, error);
294
+
295
+ throw error;
296
+ }
297
+ });
298
+ }
299
+
300
+ /**
301
+ * Implements this function. When called, it should stop recording. This is called before the `IAudioStreamNode.detach` function.
302
+ *
303
+ * Note: when using with Direct Line Speech, this function is never called.
304
+ */
305
+
306
+ // ESLint: We are not implementing this function because it is not called by Direct Line Speech.
307
+ // eslint-disable-next-line class-methods-use-this
308
+ protected performTurnOff(): Promise<void> {
309
+ // ESLint: "return" is required by TypeScript
310
+ // eslint-disable-next-line no-useless-return
311
+ return;
312
+ }
313
+
314
+ /** Turn off the audio device. This is called before detaching from the graph. */
315
+ // Speech SDK quirks: It is confused to have both "turnOff" and "detach". "turnOff" is called before "detach".
316
+ // Why don't we put all logics at "detach"?
317
+ // Speech SDK quirks: Direct Line Speech never call "turnOff". "Source off" event need to be emitted during "detach" instead.
318
+ // Also, custom implementation should be done at "detach" instead, such as ending and closing output streams.
319
+ async turnOff(): Promise<void> {
320
+ this.debug(`Callback for "turnOff".`);
321
+
322
+ await this.performTurnOff();
323
+ }
324
+
325
+ /** Gets the device information. */
326
+ // ESLint: This code will only works in browsers other than IE11. Only works in ES5 is okay.
327
+ // @ts-ignore Accessors are only available when targeting ECMAScript 5 and higher.ts(1056)
328
+ get deviceInfo(): Promise<ISpeechConfigAudioDevice> {
329
+ this.debug(`Getting "deviceInfo".`);
330
+
331
+ // False alarm: indexer is a constant of type Symbol.
332
+ // eslint-disable-next-line security/detect-object-injection
333
+ return Promise.all([this[SYMBOL_DEVICE_INFO_DEFERRED].promise, this[SYMBOL_FORMAT_DEFERRED].promise]).then(
334
+ ([{ connectivity, manufacturer, model, type }, { bitsPerSample, channels, samplesPerSec }]) => ({
335
+ bitspersample: bitsPerSample,
336
+ channelcount: channels,
337
+ connectivity:
338
+ typeof connectivity === 'string' && !isForbiddenPropertyName(connectivity)
339
+ ? // Mitigated through denylisting.
340
+ // eslint-disable-next-line security/detect-object-injection
341
+ Connectivity[connectivity]
342
+ : connectivity || Connectivity.Unknown,
343
+ manufacturer: manufacturer || '',
344
+ model: model || '',
345
+ samplerate: samplesPerSec,
346
+ // Mitigated through denylisting.
347
+ // eslint-disable-next-line security/detect-object-injection
348
+ type: typeof type === 'string' && !isForbiddenPropertyName(type) ? Type[type] : type || Type.Unknown
349
+ })
350
+ );
351
+ }
352
+ }
353
+
354
+ export default CustomAudioInputStream;
355
+
356
+ export type { AudioStreamNode, DeviceInfo, Format, Options };
@@ -0,0 +1,4 @@
1
+ export default function bytesPerSample(settings: MediaTrackSettings) {
2
+ // eslint-disable-next-line no-magic-numbers
3
+ return ((settings.sampleSize as number) >> 3) * (settings.channelCount as number) * (settings.sampleRate as number);
4
+ }
@@ -0,0 +1,23 @@
1
+ /* eslint no-empty-function: "off" */
2
+
3
+ import createAudioConfig from './createAudioConfig';
4
+
5
+ describe('Verify arguments', () => {
6
+ test('should not throw if only "attach" is supplied', () => {
7
+ expect(() => createAudioConfig({ attach: () => {} })).not.toThrow();
8
+ });
9
+
10
+ test('should not throw if both "attach" and "turnOff" are supplied', () => {
11
+ expect(() => createAudioConfig({ attach: () => {}, turnOff: () => {} })).not.toThrow();
12
+ });
13
+
14
+ test('should throw if "attach" is not supplied', () => {
15
+ expect(() => createAudioConfig({})).toThrow('"attach" must be a function.');
16
+ });
17
+
18
+ test('should throw if "turnOff" is not a function', () => {
19
+ expect(() => createAudioConfig({ attach: () => {}, turnOff: '123' })).toThrow(
20
+ '"turnOff", if defined, must be a function.'
21
+ );
22
+ });
23
+ });
@@ -0,0 +1,77 @@
1
+ // TODO: [P2] #3976 We should export this to allow web developers to bring in their own microphone.
2
+ // For example, it should enable React Native devs to bring in their microphone implementation and use Cognitive Services Speech Services.
3
+
4
+ import { AudioConfig } from 'microsoft-cognitiveservices-speech-sdk';
5
+
6
+ import CustomAudioInputStream, { AudioStreamNode, DeviceInfo, Format } from './CustomAudioInputStream';
7
+
8
+ type AttachFunction = (audioNodeId: string) => Promise<{
9
+ audioStreamNode: AudioStreamNode;
10
+ deviceInfo: DeviceInfo;
11
+ format: Format;
12
+ }>;
13
+
14
+ type TurnOffFunction = () => Promise<void>;
15
+
16
+ const SYMBOL_ATTACH = Symbol('attach');
17
+ const SYMBOL_TURN_OFF = Symbol('turnOff');
18
+
19
+ type CreateAudioConfigOptions = {
20
+ /** Callback function for attaching the device by returning an audio node. */
21
+ attach: AttachFunction;
22
+
23
+ /** `true` to enable diagnostic information, otherwise, `false`. */
24
+ debug?: true;
25
+
26
+ /**
27
+ * Callback function for turning off the device before detaching its node from an audio graph.
28
+ *
29
+ * Note: this is not called for Direct Line Speech.
30
+ */
31
+ turnOff?: TurnOffFunction;
32
+ };
33
+
34
+ class CreateAudioConfigAudioInputStream extends CustomAudioInputStream {
35
+ constructor({ attach, debug, turnOff }: CreateAudioConfigOptions) {
36
+ if (!attach || typeof attach !== 'function') {
37
+ throw new Error('"attach" must be a function.');
38
+ }
39
+
40
+ if (turnOff && typeof turnOff !== 'function') {
41
+ throw new Error('"turnOff", if defined, must be a function.');
42
+ }
43
+
44
+ super({ debug });
45
+
46
+ // False alarm: indexer is a constant of type Symbol.
47
+ // eslint-disable-next-line security/detect-object-injection
48
+ this[SYMBOL_ATTACH] = attach;
49
+
50
+ // False alarm: indexer is a constant of type Symbol.
51
+ // eslint-disable-next-line security/detect-object-injection
52
+ this[SYMBOL_TURN_OFF] = turnOff;
53
+ }
54
+
55
+ [SYMBOL_ATTACH]: AttachFunction;
56
+ [SYMBOL_TURN_OFF]: TurnOffFunction;
57
+
58
+ protected performAttach(audioNodeId: string): Promise<{
59
+ audioStreamNode: AudioStreamNode;
60
+ deviceInfo: DeviceInfo;
61
+ format: Format;
62
+ }> {
63
+ // False alarm: indexer is a constant of type Symbol.
64
+ // eslint-disable-next-line security/detect-object-injection
65
+ return this[SYMBOL_ATTACH](audioNodeId);
66
+ }
67
+
68
+ protected performTurnOff(): Promise<void> {
69
+ const { [SYMBOL_TURN_OFF]: turnOff } = this;
70
+
71
+ return turnOff && turnOff();
72
+ }
73
+ }
74
+
75
+ export default function createAudioConfig(options: CreateAudioConfigOptions) {
76
+ return AudioConfig.fromStreamInput(new CreateAudioConfigAudioInputStream(options));
77
+ }
@@ -0,0 +1,16 @@
1
+ /** Creates an AudioContext object. */
2
+ export default function createAudioContext(): AudioContext {
3
+ if (typeof window.AudioContext !== 'undefined') {
4
+ return new window.AudioContext();
5
+
6
+ // Required by TypeScript.
7
+ // eslint-disable-next-line dot-notation
8
+ } else if (typeof window['webkitAudioContext'] !== 'undefined') {
9
+ // This is for Safari as Web Audio API is still under vendor-prefixed.
10
+ // eslint-disable-next-line dot-notation
11
+ return new window['webkitAudioContext']();
12
+ }
13
+
14
+ // TODO: Fix this.
15
+ throw new Error('This browser does not support Web Audio API.');
16
+ }