@thoughtspot/visual-embed-sdk 1.52.0 → 1.52.2-beta

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 (385) hide show
  1. package/cjs/package.json +19 -4
  2. package/cjs/src/contracts/contracts.spec.d.ts +2 -0
  3. package/cjs/src/contracts/contracts.spec.d.ts.map +1 -0
  4. package/cjs/src/contracts/contracts.spec.js +345 -0
  5. package/cjs/src/contracts/contracts.spec.js.map +1 -0
  6. package/cjs/src/contracts/embed-event-payloads.d.ts +73 -0
  7. package/cjs/src/contracts/embed-event-payloads.d.ts.map +1 -0
  8. package/cjs/src/contracts/embed-event-payloads.js +3 -0
  9. package/cjs/src/contracts/embed-event-payloads.js.map +1 -0
  10. package/cjs/src/contracts/host-event-contracts.d.ts +416 -0
  11. package/cjs/src/contracts/host-event-contracts.d.ts.map +1 -0
  12. package/cjs/src/contracts/host-event-contracts.js +3 -0
  13. package/cjs/src/contracts/host-event-contracts.js.map +1 -0
  14. package/cjs/src/contracts/index.d.ts +22 -0
  15. package/cjs/src/contracts/index.d.ts.map +1 -0
  16. package/cjs/src/contracts/index.js +33 -0
  17. package/cjs/src/contracts/index.js.map +1 -0
  18. package/cjs/src/contracts/ui-passthrough-contracts.d.ts +277 -0
  19. package/cjs/src/contracts/ui-passthrough-contracts.d.ts.map +1 -0
  20. package/cjs/src/{embed/hostEventClient/contracts.js → contracts/ui-passthrough-contracts.js} +6 -2
  21. package/cjs/src/contracts/ui-passthrough-contracts.js.map +1 -0
  22. package/cjs/src/embed/app.d.ts +21 -143
  23. package/cjs/src/embed/app.d.ts.map +1 -1
  24. package/cjs/src/embed/app.js +21 -121
  25. package/cjs/src/embed/app.js.map +1 -1
  26. package/cjs/src/embed/app.spec.js +80 -26
  27. package/cjs/src/embed/app.spec.js.map +1 -1
  28. package/cjs/src/embed/auto-frame-renderer.js +1 -1
  29. package/cjs/src/embed/auto-frame-renderer.js.map +1 -1
  30. package/cjs/src/embed/auto-frame-renderer.spec.js +24 -13
  31. package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
  32. package/cjs/src/embed/bodyless-conversation.js +1 -1
  33. package/cjs/src/embed/bodyless-conversation.js.map +1 -1
  34. package/cjs/src/embed/conversation.d.ts +1 -0
  35. package/cjs/src/embed/conversation.d.ts.map +1 -1
  36. package/cjs/src/embed/conversation.js +4 -4
  37. package/cjs/src/embed/conversation.js.map +1 -1
  38. package/cjs/src/embed/conversation.spec.js +35 -24
  39. package/cjs/src/embed/conversation.spec.js.map +1 -1
  40. package/cjs/src/embed/embedConfig.spec.js +2 -1
  41. package/cjs/src/embed/embedConfig.spec.js.map +1 -1
  42. package/cjs/src/embed/events.spec.js +12 -2
  43. package/cjs/src/embed/events.spec.js.map +1 -1
  44. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +5 -4
  45. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  46. package/cjs/src/embed/hostEventClient/host-event-client.js +34 -23
  47. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  48. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +50 -44
  49. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  50. package/cjs/src/embed/hostEventClient/utils.d.ts +10 -1
  51. package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
  52. package/cjs/src/embed/hostEventClient/utils.js +52 -6
  53. package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
  54. package/cjs/src/embed/hostEventClient/utils.spec.js +151 -0
  55. package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
  56. package/cjs/src/embed/liveboard.d.ts +13 -164
  57. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  58. package/cjs/src/embed/liveboard.js +72 -135
  59. package/cjs/src/embed/liveboard.js.map +1 -1
  60. package/cjs/src/embed/liveboard.spec.js +213 -29
  61. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  62. package/cjs/src/embed/search-bar.js +1 -1
  63. package/cjs/src/embed/search-bar.js.map +1 -1
  64. package/cjs/src/embed/search.d.ts +14 -0
  65. package/cjs/src/embed/search.d.ts.map +1 -1
  66. package/cjs/src/embed/search.js +5 -2
  67. package/cjs/src/embed/search.js.map +1 -1
  68. package/cjs/src/embed/search.spec.js +85 -0
  69. package/cjs/src/embed/search.spec.js.map +1 -1
  70. package/cjs/src/embed/spotter-utils.d.ts +8 -1
  71. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  72. package/cjs/src/embed/spotter-utils.js +14 -1
  73. package/cjs/src/embed/spotter-utils.js.map +1 -1
  74. package/cjs/src/embed/spotter-utils.spec.js +26 -0
  75. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  76. package/cjs/src/embed/ts-embed.d.ts +20 -22
  77. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  78. package/cjs/src/embed/ts-embed.js +171 -66
  79. package/cjs/src/embed/ts-embed.js.map +1 -1
  80. package/cjs/src/embed/ts-embed.spec.js +486 -38
  81. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  82. package/cjs/src/errors.js +1 -1
  83. package/cjs/src/errors.js.map +1 -1
  84. package/cjs/src/full-height.d.ts +137 -0
  85. package/cjs/src/full-height.d.ts.map +1 -0
  86. package/cjs/src/full-height.js +272 -0
  87. package/cjs/src/full-height.js.map +1 -0
  88. package/cjs/src/full-height.spec.d.ts +2 -0
  89. package/cjs/src/full-height.spec.d.ts.map +1 -0
  90. package/cjs/src/full-height.spec.js +835 -0
  91. package/cjs/src/full-height.spec.js.map +1 -0
  92. package/cjs/src/index.d.ts +5 -3
  93. package/cjs/src/index.d.ts.map +1 -1
  94. package/cjs/src/index.js +6 -4
  95. package/cjs/src/index.js.map +1 -1
  96. package/cjs/src/react/all-types-export.d.ts +1 -1
  97. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  98. package/cjs/src/react/all-types-export.js +2 -1
  99. package/cjs/src/react/all-types-export.js.map +1 -1
  100. package/cjs/src/react/all-types-export.parity.spec.js +55 -3
  101. package/cjs/src/react/all-types-export.parity.spec.js.map +1 -1
  102. package/cjs/src/types.d.ts +584 -24
  103. package/cjs/src/types.d.ts.map +1 -1
  104. package/cjs/src/types.js +370 -22
  105. package/cjs/src/types.js.map +1 -1
  106. package/cjs/src/utils/authService/tokenizedAuthService.spec.js +2 -1
  107. package/cjs/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  108. package/cjs/src/utils/custom-actions.spec.js +4 -2
  109. package/cjs/src/utils/custom-actions.spec.js.map +1 -1
  110. package/cjs/src/utils/filterConverter.d.ts +106 -0
  111. package/cjs/src/utils/filterConverter.d.ts.map +1 -0
  112. package/cjs/src/utils/filterConverter.js +215 -0
  113. package/cjs/src/utils/filterConverter.js.map +1 -0
  114. package/cjs/src/utils/filterConverter.spec.d.ts +2 -0
  115. package/cjs/src/utils/filterConverter.spec.d.ts.map +1 -0
  116. package/cjs/src/utils/filterConverter.spec.js +709 -0
  117. package/cjs/src/utils/filterConverter.spec.js.map +1 -0
  118. package/cjs/src/utils/processData.spec.js +2 -1
  119. package/cjs/src/utils/processData.spec.js.map +1 -1
  120. package/cjs/src/utils/processTrigger.d.ts +3 -1
  121. package/cjs/src/utils/processTrigger.d.ts.map +1 -1
  122. package/cjs/src/utils/processTrigger.js +19 -51
  123. package/cjs/src/utils/processTrigger.js.map +1 -1
  124. package/cjs/src/utils/transport/iframe-transport.d.ts +55 -0
  125. package/cjs/src/utils/transport/iframe-transport.d.ts.map +1 -0
  126. package/cjs/src/utils/transport/iframe-transport.js +108 -0
  127. package/cjs/src/utils/transport/iframe-transport.js.map +1 -0
  128. package/cjs/src/utils/transport/iframe-transport.spec.d.ts +2 -0
  129. package/cjs/src/utils/transport/iframe-transport.spec.d.ts.map +1 -0
  130. package/cjs/src/utils/transport/iframe-transport.spec.js +140 -0
  131. package/cjs/src/utils/transport/iframe-transport.spec.js.map +1 -0
  132. package/cjs/src/utils.d.ts +18 -0
  133. package/cjs/src/utils.d.ts.map +1 -1
  134. package/cjs/src/utils.js +39 -1
  135. package/cjs/src/utils.js.map +1 -1
  136. package/cjs/src/utils.spec.js +62 -0
  137. package/cjs/src/utils.spec.js.map +1 -1
  138. package/dist/{index-BMppCU63.js → index-CcBYlGo9.js} +1 -1
  139. package/dist/src/contracts/contracts.spec.d.ts +2 -0
  140. package/dist/src/contracts/contracts.spec.d.ts.map +1 -0
  141. package/dist/src/contracts/embed-event-payloads.d.ts +73 -0
  142. package/dist/src/contracts/embed-event-payloads.d.ts.map +1 -0
  143. package/dist/src/contracts/host-event-contracts.d.ts +416 -0
  144. package/dist/src/contracts/host-event-contracts.d.ts.map +1 -0
  145. package/dist/src/contracts/index.d.ts +22 -0
  146. package/dist/src/contracts/index.d.ts.map +1 -0
  147. package/dist/src/contracts/ui-passthrough-contracts.d.ts +277 -0
  148. package/dist/src/contracts/ui-passthrough-contracts.d.ts.map +1 -0
  149. package/dist/src/embed/app.d.ts +21 -143
  150. package/dist/src/embed/app.d.ts.map +1 -1
  151. package/dist/src/embed/conversation.d.ts +1 -0
  152. package/dist/src/embed/conversation.d.ts.map +1 -1
  153. package/dist/src/embed/hostEventClient/host-event-client.d.ts +5 -4
  154. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  155. package/dist/src/embed/hostEventClient/utils.d.ts +10 -1
  156. package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
  157. package/dist/src/embed/liveboard.d.ts +13 -164
  158. package/dist/src/embed/liveboard.d.ts.map +1 -1
  159. package/dist/src/embed/search.d.ts +14 -0
  160. package/dist/src/embed/search.d.ts.map +1 -1
  161. package/dist/src/embed/spotter-utils.d.ts +8 -1
  162. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  163. package/dist/src/embed/ts-embed.d.ts +20 -22
  164. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  165. package/dist/src/full-height.d.ts +137 -0
  166. package/dist/src/full-height.d.ts.map +1 -0
  167. package/dist/src/full-height.spec.d.ts +2 -0
  168. package/dist/src/full-height.spec.d.ts.map +1 -0
  169. package/dist/src/index.d.ts +5 -3
  170. package/dist/src/index.d.ts.map +1 -1
  171. package/dist/src/react/all-types-export.d.ts +1 -1
  172. package/dist/src/react/all-types-export.d.ts.map +1 -1
  173. package/dist/src/types.d.ts +584 -24
  174. package/dist/src/types.d.ts.map +1 -1
  175. package/dist/src/utils/filterConverter.d.ts +106 -0
  176. package/dist/src/utils/filterConverter.d.ts.map +1 -0
  177. package/dist/src/utils/filterConverter.spec.d.ts +2 -0
  178. package/dist/src/utils/filterConverter.spec.d.ts.map +1 -0
  179. package/dist/src/utils/processTrigger.d.ts +3 -1
  180. package/dist/src/utils/processTrigger.d.ts.map +1 -1
  181. package/dist/src/utils/transport/iframe-transport.d.ts +55 -0
  182. package/dist/src/utils/transport/iframe-transport.d.ts.map +1 -0
  183. package/dist/src/utils/transport/iframe-transport.spec.d.ts +2 -0
  184. package/dist/src/utils/transport/iframe-transport.spec.d.ts.map +1 -0
  185. package/dist/src/utils.d.ts +18 -0
  186. package/dist/src/utils.d.ts.map +1 -1
  187. package/dist/tsembed-react.es.js +1144 -412
  188. package/dist/tsembed-react.js +1143 -411
  189. package/dist/tsembed.es.js +1357 -418
  190. package/dist/tsembed.js +1352 -412
  191. package/dist/visual-embed-sdk-react-full.d.ts +1514 -651
  192. package/dist/visual-embed-sdk-react.d.ts +1351 -632
  193. package/dist/visual-embed-sdk.d.ts +1508 -668
  194. package/lib/package.json +19 -4
  195. package/lib/src/contracts/contracts.spec.d.ts +2 -0
  196. package/lib/src/contracts/contracts.spec.d.ts.map +1 -0
  197. package/lib/src/contracts/contracts.spec.js +342 -0
  198. package/lib/src/contracts/contracts.spec.js.map +1 -0
  199. package/lib/src/contracts/embed-event-payloads.d.ts +73 -0
  200. package/lib/src/contracts/embed-event-payloads.d.ts.map +1 -0
  201. package/lib/src/contracts/embed-event-payloads.js +2 -0
  202. package/lib/src/contracts/embed-event-payloads.js.map +1 -0
  203. package/lib/src/contracts/host-event-contracts.d.ts +416 -0
  204. package/lib/src/contracts/host-event-contracts.d.ts.map +1 -0
  205. package/lib/src/contracts/host-event-contracts.js +2 -0
  206. package/lib/src/contracts/host-event-contracts.js.map +1 -0
  207. package/lib/src/contracts/index.d.ts +22 -0
  208. package/lib/src/contracts/index.d.ts.map +1 -0
  209. package/lib/src/contracts/index.js +22 -0
  210. package/lib/src/contracts/index.js.map +1 -0
  211. package/lib/src/contracts/ui-passthrough-contracts.d.ts +277 -0
  212. package/lib/src/contracts/ui-passthrough-contracts.d.ts.map +1 -0
  213. package/lib/src/{embed/hostEventClient/contracts.js → contracts/ui-passthrough-contracts.js} +6 -2
  214. package/lib/src/contracts/ui-passthrough-contracts.js.map +1 -0
  215. package/lib/src/embed/app.d.ts +21 -143
  216. package/lib/src/embed/app.d.ts.map +1 -1
  217. package/lib/src/embed/app.js +23 -123
  218. package/lib/src/embed/app.js.map +1 -1
  219. package/lib/src/embed/app.spec.js +80 -26
  220. package/lib/src/embed/app.spec.js.map +1 -1
  221. package/lib/src/embed/auto-frame-renderer.js +1 -1
  222. package/lib/src/embed/auto-frame-renderer.js.map +1 -1
  223. package/lib/src/embed/auto-frame-renderer.spec.js +24 -13
  224. package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
  225. package/lib/src/embed/bodyless-conversation.js +1 -1
  226. package/lib/src/embed/bodyless-conversation.js.map +1 -1
  227. package/lib/src/embed/conversation.d.ts +1 -0
  228. package/lib/src/embed/conversation.d.ts.map +1 -1
  229. package/lib/src/embed/conversation.js +5 -5
  230. package/lib/src/embed/conversation.js.map +1 -1
  231. package/lib/src/embed/conversation.spec.js +35 -24
  232. package/lib/src/embed/conversation.spec.js.map +1 -1
  233. package/lib/src/embed/embedConfig.spec.js +2 -1
  234. package/lib/src/embed/embedConfig.spec.js.map +1 -1
  235. package/lib/src/embed/events.spec.js +12 -2
  236. package/lib/src/embed/events.spec.js.map +1 -1
  237. package/lib/src/embed/hostEventClient/host-event-client.d.ts +5 -4
  238. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  239. package/lib/src/embed/hostEventClient/host-event-client.js +19 -8
  240. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  241. package/lib/src/embed/hostEventClient/host-event-client.spec.js +15 -9
  242. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  243. package/lib/src/embed/hostEventClient/utils.d.ts +10 -1
  244. package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
  245. package/lib/src/embed/hostEventClient/utils.js +48 -3
  246. package/lib/src/embed/hostEventClient/utils.js.map +1 -1
  247. package/lib/src/embed/hostEventClient/utils.spec.js +152 -1
  248. package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
  249. package/lib/src/embed/liveboard.d.ts +13 -164
  250. package/lib/src/embed/liveboard.d.ts.map +1 -1
  251. package/lib/src/embed/liveboard.js +73 -136
  252. package/lib/src/embed/liveboard.js.map +1 -1
  253. package/lib/src/embed/liveboard.spec.js +213 -29
  254. package/lib/src/embed/liveboard.spec.js.map +1 -1
  255. package/lib/src/embed/search-bar.js +1 -1
  256. package/lib/src/embed/search-bar.js.map +1 -1
  257. package/lib/src/embed/search.d.ts +14 -0
  258. package/lib/src/embed/search.d.ts.map +1 -1
  259. package/lib/src/embed/search.js +5 -2
  260. package/lib/src/embed/search.js.map +1 -1
  261. package/lib/src/embed/search.spec.js +85 -0
  262. package/lib/src/embed/search.spec.js.map +1 -1
  263. package/lib/src/embed/spotter-utils.d.ts +8 -1
  264. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  265. package/lib/src/embed/spotter-utils.js +12 -0
  266. package/lib/src/embed/spotter-utils.js.map +1 -1
  267. package/lib/src/embed/spotter-utils.spec.js +27 -1
  268. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  269. package/lib/src/embed/ts-embed.d.ts +20 -22
  270. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  271. package/lib/src/embed/ts-embed.js +171 -66
  272. package/lib/src/embed/ts-embed.js.map +1 -1
  273. package/lib/src/embed/ts-embed.spec.js +484 -36
  274. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  275. package/lib/src/errors.js +1 -1
  276. package/lib/src/errors.js.map +1 -1
  277. package/lib/src/full-height.d.ts +137 -0
  278. package/lib/src/full-height.d.ts.map +1 -0
  279. package/lib/src/full-height.js +268 -0
  280. package/lib/src/full-height.js.map +1 -0
  281. package/lib/src/full-height.spec.d.ts +2 -0
  282. package/lib/src/full-height.spec.d.ts.map +1 -0
  283. package/lib/src/full-height.spec.js +833 -0
  284. package/lib/src/full-height.spec.js.map +1 -0
  285. package/lib/src/index.d.ts +5 -3
  286. package/lib/src/index.d.ts.map +1 -1
  287. package/lib/src/index.js +3 -2
  288. package/lib/src/index.js.map +1 -1
  289. package/lib/src/react/all-types-export.d.ts +1 -1
  290. package/lib/src/react/all-types-export.d.ts.map +1 -1
  291. package/lib/src/react/all-types-export.js +1 -1
  292. package/lib/src/react/all-types-export.js.map +1 -1
  293. package/lib/src/react/all-types-export.parity.spec.js +55 -3
  294. package/lib/src/react/all-types-export.parity.spec.js.map +1 -1
  295. package/lib/src/types.d.ts +584 -24
  296. package/lib/src/types.d.ts.map +1 -1
  297. package/lib/src/types.js +370 -22
  298. package/lib/src/types.js.map +1 -1
  299. package/lib/src/utils/authService/tokenizedAuthService.spec.js +2 -1
  300. package/lib/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  301. package/lib/src/utils/custom-actions.spec.js +4 -2
  302. package/lib/src/utils/custom-actions.spec.js.map +1 -1
  303. package/lib/src/utils/filterConverter.d.ts +106 -0
  304. package/lib/src/utils/filterConverter.d.ts.map +1 -0
  305. package/lib/src/utils/filterConverter.js +210 -0
  306. package/lib/src/utils/filterConverter.js.map +1 -0
  307. package/lib/src/utils/filterConverter.spec.d.ts +2 -0
  308. package/lib/src/utils/filterConverter.spec.d.ts.map +1 -0
  309. package/lib/src/utils/filterConverter.spec.js +707 -0
  310. package/lib/src/utils/filterConverter.spec.js.map +1 -0
  311. package/lib/src/utils/processData.spec.js +2 -1
  312. package/lib/src/utils/processData.spec.js.map +1 -1
  313. package/lib/src/utils/processTrigger.d.ts +3 -1
  314. package/lib/src/utils/processTrigger.d.ts.map +1 -1
  315. package/lib/src/utils/processTrigger.js +19 -51
  316. package/lib/src/utils/processTrigger.js.map +1 -1
  317. package/lib/src/utils/transport/iframe-transport.d.ts +55 -0
  318. package/lib/src/utils/transport/iframe-transport.d.ts.map +1 -0
  319. package/lib/src/utils/transport/iframe-transport.js +101 -0
  320. package/lib/src/utils/transport/iframe-transport.js.map +1 -0
  321. package/lib/src/utils/transport/iframe-transport.spec.d.ts +2 -0
  322. package/lib/src/utils/transport/iframe-transport.spec.d.ts.map +1 -0
  323. package/lib/src/utils/transport/iframe-transport.spec.js +138 -0
  324. package/lib/src/utils/transport/iframe-transport.spec.js.map +1 -0
  325. package/lib/src/utils.d.ts +18 -0
  326. package/lib/src/utils.d.ts.map +1 -1
  327. package/lib/src/utils.js +37 -0
  328. package/lib/src/utils.js.map +1 -1
  329. package/lib/src/utils.spec.js +63 -1
  330. package/lib/src/utils.spec.js.map +1 -1
  331. package/lib/src/visual-embed-sdk.d.ts +1508 -668
  332. package/package.json +19 -4
  333. package/src/contracts/contracts.spec.ts +392 -0
  334. package/src/contracts/embed-event-payloads.ts +83 -0
  335. package/src/contracts/host-event-contracts.ts +500 -0
  336. package/src/contracts/index.ts +53 -0
  337. package/src/contracts/ui-passthrough-contracts.ts +301 -0
  338. package/src/embed/app.spec.ts +101 -34
  339. package/src/embed/app.ts +42 -268
  340. package/src/embed/auto-frame-renderer.spec.ts +24 -13
  341. package/src/embed/auto-frame-renderer.ts +1 -1
  342. package/src/embed/bodyless-conversation.ts +1 -1
  343. package/src/embed/conversation.spec.ts +38 -27
  344. package/src/embed/conversation.ts +5 -5
  345. package/src/embed/embedConfig.spec.ts +2 -1
  346. package/src/embed/events.spec.ts +14 -2
  347. package/src/embed/hostEventClient/host-event-client.spec.ts +16 -10
  348. package/src/embed/hostEventClient/host-event-client.ts +31 -10
  349. package/src/embed/hostEventClient/utils.spec.ts +176 -1
  350. package/src/embed/hostEventClient/utils.ts +53 -3
  351. package/src/embed/liveboard.spec.ts +261 -41
  352. package/src/embed/liveboard.ts +85 -300
  353. package/src/embed/search-bar.tsx +1 -1
  354. package/src/embed/search.spec.ts +103 -0
  355. package/src/embed/search.ts +21 -1
  356. package/src/embed/spotter-utils.spec.ts +29 -0
  357. package/src/embed/spotter-utils.ts +16 -0
  358. package/src/embed/ts-embed.spec.ts +650 -36
  359. package/src/embed/ts-embed.ts +205 -76
  360. package/src/errors.ts +1 -1
  361. package/src/full-height.spec.ts +1008 -0
  362. package/src/full-height.ts +361 -0
  363. package/src/index.ts +103 -5
  364. package/src/react/all-types-export.parity.spec.ts +63 -3
  365. package/src/react/all-types-export.ts +49 -0
  366. package/src/types.ts +584 -22
  367. package/src/utils/authService/tokenizedAuthService.spec.ts +2 -1
  368. package/src/utils/custom-actions.spec.ts +4 -2
  369. package/src/utils/filterConverter.spec.ts +801 -0
  370. package/src/utils/filterConverter.ts +304 -0
  371. package/src/utils/processData.spec.ts +2 -1
  372. package/src/utils/processTrigger.ts +28 -58
  373. package/src/utils/transport/iframe-transport.spec.ts +177 -0
  374. package/src/utils/transport/iframe-transport.ts +122 -0
  375. package/src/utils.spec.ts +71 -0
  376. package/src/utils.ts +38 -0
  377. package/cjs/src/embed/hostEventClient/contracts.d.ts +0 -231
  378. package/cjs/src/embed/hostEventClient/contracts.d.ts.map +0 -1
  379. package/cjs/src/embed/hostEventClient/contracts.js.map +0 -1
  380. package/dist/src/embed/hostEventClient/contracts.d.ts +0 -231
  381. package/dist/src/embed/hostEventClient/contracts.d.ts.map +0 -1
  382. package/lib/src/embed/hostEventClient/contracts.d.ts +0 -231
  383. package/lib/src/embed/hostEventClient/contracts.d.ts.map +0 -1
  384. package/lib/src/embed/hostEventClient/contracts.js.map +0 -1
  385. package/src/embed/hostEventClient/contracts.ts +0 -262
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Copyright (c) 2026
3
+ *
4
+ * Utility to convert the payload emitted by `EmbedEvent.FilterChanged`
5
+ * into the payload shape expected by `HostEvent.UpdateFilters`, so that
6
+ * the current filter state of a Liveboard can be captured and re-applied
7
+ * later without having to hand-parse the event payload.
8
+ * @summary Filter payload converter
9
+ */
10
+
11
+ import isNil from 'lodash/isNil';
12
+ import { RuntimeFilter, RuntimeFilterOp } from '../types';
13
+ import { Applicability, ApplicabilityLevel } from '../contracts/ui-passthrough-contracts';
14
+
15
+ export interface FilterChangedFilterContentValue {
16
+ key?: string | number | boolean | null;
17
+ }
18
+
19
+ export interface FilterChangedFilterContent {
20
+ filterType?: string;
21
+ negate?: boolean;
22
+ value?: FilterChangedFilterContentValue[];
23
+ }
24
+
25
+ export interface FilterChangedDateFilterValue {
26
+ type?: string;
27
+ op?: string;
28
+ // The payload is JSON from the embedded app, so absent fields can arrive
29
+ // as `null` rather than being omitted.
30
+ epoch?: string | number | null;
31
+ dateRange?: {
32
+ lowEpoch?: string | number | null;
33
+ highEpoch?: string | number | null;
34
+ };
35
+ monthName?: string;
36
+ quarterName?: string;
37
+ yearName?: string;
38
+ number?: number | null;
39
+ datePeriod?: string;
40
+ includeCurrentPeriod?: boolean | null;
41
+ }
42
+
43
+ export interface FilterChangedDateFilterContent {
44
+ negate?: boolean;
45
+ dateFilter?: FilterChangedDateFilterValue;
46
+ }
47
+
48
+ export interface FilterChangedFilter {
49
+ filterContent?: FilterChangedFilterContent[];
50
+ dateFilterContent?: FilterChangedDateFilterContent[];
51
+ }
52
+
53
+ export interface FilterChangedFilterGroup {
54
+ columnInfo?: {
55
+ name?: string;
56
+ };
57
+ filters?: FilterChangedFilter[];
58
+ /**
59
+ * Scope of the filter - `LIVEBOARD`, `TAB` or `GROUP`. Present from
60
+ * SDK 1.53.0 / ThoughtSpot 26.10.0.cl. Omitted means Liveboard level.
61
+ */
62
+ applicability?: Applicability;
63
+ }
64
+
65
+ /**
66
+ * Shape of the payload received via `LiveboardEmbed.on(EmbedEvent.FilterChanged, ...)`.
67
+ */
68
+ export interface FilterChangedPayload {
69
+ liveboardFilters?: FilterChangedFilterGroup[];
70
+ runtimeFilters?: RuntimeFilter[];
71
+ }
72
+
73
+ export interface UpdateFiltersFilterParam {
74
+ columnName: string;
75
+ operator: string;
76
+ values: (string | number | boolean | bigint)[];
77
+ type?: string;
78
+ datePeriod?: string;
79
+ negate?: boolean;
80
+ includeCurrentPeriod?: boolean;
81
+ applicability?: Applicability;
82
+ }
83
+
84
+ /**
85
+ * Shape expected by `liveboardEmbed.trigger(HostEvent.UpdateFilters, ...)`.
86
+ */
87
+ export interface UpdateFiltersPayload {
88
+ filters: UpdateFiltersFilterParam[];
89
+ }
90
+
91
+ type DateFilterValueExtractor = (dateFilter: FilterChangedDateFilterValue) => (string | number)[];
92
+
93
+ /**
94
+ * Coerces an epoch/count field to a number, returning `null` when the value is
95
+ * absent or not a finite number. The payload arrives as JSON from the iframe,
96
+ * so a missing field can be `null` as well as `undefined`, and `Number(null)`
97
+ * or `Number('')` would otherwise silently become a valid-looking `0`
98
+ * (i.e. the Unix epoch, or a zero-length period).
99
+ * @param value Raw value from the date filter payload.
100
+ */
101
+ function toFiniteNumber(value: string | number | undefined | null): number | null {
102
+ if (isNil(value) || value === '') return null;
103
+ const numericValue = Number(value);
104
+ return Number.isFinite(numericValue) ? numericValue : null;
105
+ }
106
+
107
+ const DATE_FILTER_VALUE_EXTRACTORS: Record<string, DateFilterValueExtractor> = {
108
+ EXACT_DATE: (dateFilter) => {
109
+ const epoch = toFiniteNumber(dateFilter.epoch);
110
+ return epoch === null ? [] : [epoch];
111
+ },
112
+ EXACT_DATE_RANGE: (dateFilter) => {
113
+ const { lowEpoch, highEpoch } = dateFilter.dateRange ?? {};
114
+ const low = toFiniteNumber(lowEpoch);
115
+ const high = toFiniteNumber(highEpoch);
116
+ return low === null || high === null ? [] : [low, high];
117
+ },
118
+ MONTH_YEAR: (dateFilter) => (
119
+ dateFilter.monthName && dateFilter.yearName
120
+ ? [dateFilter.monthName, dateFilter.yearName]
121
+ : []
122
+ ),
123
+ QUARTER_YEAR: (dateFilter) => (
124
+ dateFilter.quarterName && dateFilter.yearName
125
+ ? [dateFilter.quarterName, dateFilter.yearName]
126
+ : []
127
+ ),
128
+ YEAR_ONLY: (dateFilter) => (dateFilter.yearName ? [dateFilter.yearName] : []),
129
+ LAST_N_PERIOD: (dateFilter) => {
130
+ const count = toFiniteNumber(dateFilter.number);
131
+ return count === null ? [] : [count];
132
+ },
133
+ NEXT_N_PERIOD: (dateFilter) => {
134
+ const count = toFiniteNumber(dateFilter.number);
135
+ return count === null ? [] : [count];
136
+ },
137
+ };
138
+
139
+ // Date filter types with no `values` (e.g. TODAY needs no operand).
140
+ const PERIOD_ONLY_DATE_FILTER_TYPES = new Set([
141
+ 'THIS_PERIOD', 'PERIOD_TO_DATE', 'TODAY', 'YESTERDAY', 'TOMORROW',
142
+ ]);
143
+
144
+ function convertDateFilterToParam(
145
+ columnName: string,
146
+ dateFilterContent: FilterChangedDateFilterContent,
147
+ ): UpdateFiltersFilterParam | null {
148
+ const dateFilter = dateFilterContent?.dateFilter;
149
+ if (!dateFilter?.type) return null;
150
+
151
+ let values: (string | number)[];
152
+ const getValues = DATE_FILTER_VALUE_EXTRACTORS[dateFilter.type];
153
+ if (getValues) {
154
+ values = getValues(dateFilter);
155
+ // Required fields (e.g. epoch, yearName) are missing - the source data
156
+ // can't be reconstructed faithfully, so skip rather than emit a filter
157
+ // that would clear/corrupt this column when re-applied.
158
+ if (values.length === 0) return null;
159
+ } else if (PERIOD_ONLY_DATE_FILTER_TYPES.has(dateFilter.type)) {
160
+ values = [];
161
+ } else {
162
+ // Unrecognized date filter type - skip rather than guess.
163
+ return null;
164
+ }
165
+
166
+ const param: UpdateFiltersFilterParam = {
167
+ columnName,
168
+ operator: dateFilter.op ?? RuntimeFilterOp.EQ,
169
+ values,
170
+ type: dateFilter.type,
171
+ };
172
+ if (dateFilter.datePeriod) {
173
+ param.datePeriod = dateFilter.datePeriod;
174
+ }
175
+ if (!isNil(dateFilter.includeCurrentPeriod)) {
176
+ param.includeCurrentPeriod = dateFilter.includeCurrentPeriod;
177
+ }
178
+ if (dateFilterContent.negate) {
179
+ param.negate = true;
180
+ }
181
+
182
+ return param;
183
+ }
184
+
185
+ function convertFilterContentToParam(
186
+ columnName: string,
187
+ filterContent: FilterChangedFilterContent,
188
+ ): UpdateFiltersFilterParam | null {
189
+ if (!filterContent?.filterType) return null;
190
+
191
+ const values = (filterContent.value ?? [])
192
+ .map((value) => value?.key)
193
+ .filter((value): value is string | number | boolean => !isNil(value));
194
+
195
+ const param: UpdateFiltersFilterParam = {
196
+ columnName,
197
+ operator: filterContent.filterType,
198
+ values,
199
+ };
200
+ if (filterContent.negate) {
201
+ param.negate = true;
202
+ }
203
+
204
+ return param;
205
+ }
206
+
207
+ function convertFilterToParams(columnName: string, filter: FilterChangedFilter): UpdateFiltersFilterParam[] {
208
+ const dateParams = (filter?.dateFilterContent ?? [])
209
+ .map((dateFilterContent) => convertDateFilterToParam(columnName, dateFilterContent));
210
+ const contentParams = (filter?.filterContent ?? [])
211
+ .map((filterContent) => convertFilterContentToParam(columnName, filterContent));
212
+
213
+ return [...dateParams, ...contentParams]
214
+ .filter((param): param is UpdateFiltersFilterParam => param !== null);
215
+ }
216
+
217
+ /**
218
+ * Runtime filters need no reshaping, but they still come off the wire as JSON,
219
+ * so a malformed entry is possible. Skip it rather than pass it through: an
220
+ * entry missing a column, operator or values array fails
221
+ * `isValidUpdateFiltersPayload`, and that rejects the *whole* `filters` array,
222
+ * so one bad runtime filter would drop every other converted filter with it.
223
+ * @param runtimeFilter A runtime filter from the `FilterChanged` payload.
224
+ */
225
+ function convertRuntimeFilterToParam(runtimeFilter: RuntimeFilter): UpdateFiltersFilterParam | null {
226
+ const { columnName, operator, values } = runtimeFilter ?? {};
227
+ if (typeof columnName !== 'string' || !columnName) return null;
228
+ if (typeof operator !== 'string' || !operator) return null;
229
+ if (!Array.isArray(values)) return null;
230
+
231
+ return { columnName, operator, values };
232
+ }
233
+
234
+ /**
235
+ * Mirrors `isValidApplicability` in `hostEventClient/utils`: a `TAB` or `GROUP`
236
+ * scope is meaningless without a `targetId`, and `UpdateFilters` rejects the
237
+ * whole `filters` array when any entry carries a malformed `applicability`.
238
+ * @param applicability Scope taken from the `FilterChanged` payload.
239
+ */
240
+ function isUsableApplicability(applicability: Applicability): boolean {
241
+ const levels: string[] = Object.values(ApplicabilityLevel);
242
+ if (!levels.includes(applicability.level)) return false;
243
+ if (applicability.level === ApplicabilityLevel.Liveboard) return true;
244
+ return typeof applicability.targetId === 'string' && applicability.targetId.trim().length > 0;
245
+ }
246
+
247
+ function convertFilterGroupToParams(filterGroup: FilterChangedFilterGroup): UpdateFiltersFilterParam[] {
248
+ const columnName = filterGroup?.columnInfo?.name;
249
+ if (!columnName) return [];
250
+
251
+ const { applicability } = filterGroup;
252
+ // A filter scoped to a tab or group has to keep that scope. Replaying it
253
+ // without `applicability` would silently widen it to the whole Liveboard,
254
+ // changing what every other tab shows. If a scope is present but unusable,
255
+ // skip the filter: widening is worse than dropping, and a malformed
256
+ // applicability would fail validation for every other filter alongside it.
257
+ if (!isNil(applicability) && !isUsableApplicability(applicability)) return [];
258
+
259
+ const params = (filterGroup.filters ?? [])
260
+ .flatMap((filter) => convertFilterToParams(columnName, filter));
261
+
262
+ return isNil(applicability) ? params : params.map((param) => ({ ...param, applicability }));
263
+ }
264
+
265
+ /**
266
+ * Converts the payload emitted by {@link EmbedEvent.FilterChanged} into the
267
+ * payload shape expected by {@link HostEvent.UpdateFilters}, so the same
268
+ * filter state can be captured and re-applied later, for example on a
269
+ * subsequent page load.
270
+ *
271
+ * Both the Liveboard filters and the runtime filters present in the
272
+ * `FilterChanged` payload are included in the returned `filters` array.
273
+ *
274
+ * Note: a column with multiple date-filter conditions (e.g. an OR of two
275
+ * date ranges) is not guaranteed to round-trip losslessly - `HostEvent.UpdateFilters`
276
+ * treats date filters as replace-not-merge per column, so only the last
277
+ * converted entry for such a column will apply.
278
+ * @param filterChangedPayload The filter state carried by the
279
+ * `EmbedEvent.FilterChanged` callback, which is `payload.data` - the
280
+ * callback argument is the event envelope, and the filters sit under its
281
+ * `data` key.
282
+ * @example
283
+ * ```js
284
+ * let savedFilters;
285
+ * liveboardEmbed.on(EmbedEvent.FilterChanged, (payload) => {
286
+ * savedFilters = convertFilterChangedToUpdateFiltersPayload(payload.data);
287
+ * });
288
+ *
289
+ * // later, e.g. after a fresh page load
290
+ * liveboardEmbed.trigger(HostEvent.UpdateFilters, savedFilters);
291
+ * ```
292
+ */
293
+ export function convertFilterChangedToUpdateFiltersPayload(
294
+ filterChangedPayload: FilterChangedPayload,
295
+ ): UpdateFiltersPayload {
296
+ const liveboardFilterParams = (filterChangedPayload?.liveboardFilters ?? [])
297
+ .flatMap(convertFilterGroupToParams);
298
+
299
+ const runtimeFilterParams = (filterChangedPayload?.runtimeFilters ?? [])
300
+ .map(convertRuntimeFilterToParam)
301
+ .filter((param): param is UpdateFiltersFilterParam => param !== null);
302
+
303
+ return { filters: [...liveboardFilterParams, ...runtimeFilterParams] };
304
+ }
@@ -311,7 +311,8 @@ describe('Unit test for process data', () => {
311
311
  } as any);
312
312
  const el: any = {};
313
313
  processDataInstance.processEventData(e.type, e, '', el);
314
- // isEmbeddedSSOInfoFailure=true so neither branch fires, innerHTML stays unset
314
+ // isEmbeddedSSOInfoFailure=true so neither branch fires, innerHTML
315
+ // stays unset
315
316
  expect(el.innerHTML).toBeUndefined();
316
317
  expect(base.notifyAuthFailure).not.toHaveBeenCalled();
317
318
  });
@@ -1,8 +1,11 @@
1
- import { ERROR_MESSAGE } from '../errors';
2
- import { ContextType, HostEvent, MessagePayload } from '../types';
1
+ import { ContextType, HostEvent } from '../types';
3
2
  import { logger } from '../utils/logger';
4
3
  import { handlePresentEvent } from '../utils';
5
4
  import { getEmbedConfig } from '../embed/embedConfig';
5
+ import {
6
+ MESSAGE_RESPONSE_TIMEOUT,
7
+ sendMessageWithResponse,
8
+ } from './transport/iframe-transport';
6
9
 
7
10
  /**
8
11
  * Reloads the ThoughtSpot iframe.
@@ -16,29 +19,12 @@ export const reload = (iFrame: HTMLIFrameElement) => {
16
19
  }, 100);
17
20
  };
18
21
 
19
- /**
20
- * Post iframe message.
21
- * @param iFrame
22
- * @param message
23
- * @param message.type
24
- * @param message.data
25
- * @param message.context
26
- * @param thoughtSpotHost
27
- * @param channel
28
- */
29
- function postIframeMessage(
30
- iFrame: HTMLIFrameElement,
31
- message: { type: HostEvent; data: any, context?: any },
32
- thoughtSpotHost: string,
33
- channel?: MessageChannel,
34
- ) {
35
- return iFrame.contentWindow?.postMessage(message, thoughtSpotHost, [channel?.port2]);
36
- }
37
-
38
- export const TRIGGER_TIMEOUT = 30000;
22
+ export const TRIGGER_TIMEOUT = MESSAGE_RESPONSE_TIMEOUT;
39
23
 
40
24
  /**
41
- *
25
+ * Processes a host event trigger: handles SDK-local events (Reload,
26
+ * Present) and forwards everything else to the embedded app over the
27
+ * iframe transport.
42
28
  * @param iFrame
43
29
  * @param messageType
44
30
  * @param thoughtSpotHost
@@ -52,42 +38,26 @@ export function processTrigger(
52
38
  data: any,
53
39
  context?: ContextType,
54
40
  ): Promise<any> {
55
- return new Promise<any>((res, rej) => {
56
- if (messageType === HostEvent.Reload) {
57
- reload(iFrame);
58
- return res(null);
59
- }
60
-
61
- if (messageType === HostEvent.Present) {
62
- const embedConfig = getEmbedConfig();
63
- const disableFullscreenPresentation = embedConfig?.disableFullscreenPresentation ?? true;
64
-
65
- if (!disableFullscreenPresentation) {
66
- handlePresentEvent(iFrame);
67
- } else {
68
- logger.warn('Fullscreen presentation mode is disabled. Set disableFullscreenPresentation: false to enable this feature.');
69
- }
70
- }
71
-
72
- const channel = new MessageChannel();
41
+ if (messageType === HostEvent.Reload) {
42
+ reload(iFrame);
43
+ return Promise.resolve(null);
44
+ }
73
45
 
74
- // Close the messageChannel and resolve the promise if timeout.
75
- const timeoutId = setTimeout(() => {
76
- channel.port1.close();
77
- res(new Error(ERROR_MESSAGE.TRIGGER_TIMED_OUT));
78
- }, TRIGGER_TIMEOUT);
46
+ if (messageType === HostEvent.Present) {
47
+ const embedConfig = getEmbedConfig();
48
+ const disableFullscreenPresentation = embedConfig?.disableFullscreenPresentation ?? true;
79
49
 
80
- channel.port1.onmessage = ({ data: responseData }) => {
81
- clearTimeout(timeoutId);
82
- channel.port1.close();
83
- const error = responseData?.error || responseData?.data?.error;
84
- if (error) {
85
- rej(error);
86
- } else {
87
- res(responseData);
88
- }
89
- };
50
+ if (!disableFullscreenPresentation) {
51
+ handlePresentEvent(iFrame);
52
+ } else {
53
+ logger.warn('Fullscreen presentation mode is disabled. Set disableFullscreenPresentation: false to enable this feature.');
54
+ }
55
+ }
90
56
 
91
- return postIframeMessage(iFrame, { type: messageType, data, context }, thoughtSpotHost, channel);
92
- });
57
+ return sendMessageWithResponse(
58
+ iFrame,
59
+ { type: messageType, data, context },
60
+ thoughtSpotHost,
61
+ TRIGGER_TIMEOUT,
62
+ );
93
63
  }
@@ -0,0 +1,177 @@
1
+ import {
2
+ MESSAGE_RESPONSE_TIMEOUT,
3
+ resolveMessageOrigin,
4
+ isMessageFromIframe,
5
+ postMessageToIframe,
6
+ sendMessageWithResponse,
7
+ } from './iframe-transport';
8
+ import { logger } from '../logger';
9
+ import { ERROR_MESSAGE } from '../../errors';
10
+
11
+ describe('iframe-transport', () => {
12
+ describe('resolveMessageOrigin', () => {
13
+ it('returns the origin (scheme://host[:port]) for a valid host URL', () => {
14
+ expect(resolveMessageOrigin('https://my.thoughtspot.com')).toBe(
15
+ 'https://my.thoughtspot.com',
16
+ );
17
+ expect(
18
+ resolveMessageOrigin('https://host.example.com:8443/some/path'),
19
+ ).toBe('https://host.example.com:8443');
20
+ });
21
+
22
+ it('returns null when the host cannot be parsed', () => {
23
+ expect(resolveMessageOrigin('not-a-url')).toBeNull();
24
+ expect(resolveMessageOrigin('')).toBeNull();
25
+ });
26
+ });
27
+
28
+ describe('isMessageFromIframe', () => {
29
+ const host = 'https://ts.example.com';
30
+ const contentWindow = {} as Window;
31
+ const iFrame = { contentWindow } as HTMLIFrameElement;
32
+
33
+ it('rejects a message whose source is not the iframe window', () => {
34
+ const event = { source: {}, origin: host } as unknown as MessageEvent;
35
+ expect(isMessageFromIframe(event, iFrame, host)).toBe(false);
36
+ });
37
+
38
+ it('accepts a message from the iframe window with a matching origin', () => {
39
+ const event = { source: contentWindow, origin: host } as unknown as MessageEvent;
40
+ expect(isMessageFromIframe(event, iFrame, host)).toBe(true);
41
+ });
42
+
43
+ it('rejects (and warns) when the origin does not match the expected host', () => {
44
+ const warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => undefined);
45
+ const event = {
46
+ source: contentWindow,
47
+ origin: 'https://evil.example.com',
48
+ } as unknown as MessageEvent;
49
+
50
+ expect(isMessageFromIframe(event, iFrame, host)).toBe(false);
51
+ expect(warnSpy).toHaveBeenCalledTimes(1);
52
+ warnSpy.mockRestore();
53
+ });
54
+
55
+ it('fails open (accepts) when the expected origin cannot be resolved', () => {
56
+ const event = {
57
+ source: contentWindow,
58
+ origin: 'https://anything.example.com',
59
+ } as unknown as MessageEvent;
60
+ // 'garbage-host' is unparseable → origin check is skipped.
61
+ expect(isMessageFromIframe(event, iFrame, 'garbage-host')).toBe(true);
62
+ });
63
+
64
+ it('accepts when the message origin is empty', () => {
65
+ const event = { source: contentWindow, origin: '' } as unknown as MessageEvent;
66
+ expect(isMessageFromIframe(event, iFrame, host)).toBe(true);
67
+ });
68
+
69
+ it('rejects when the iframe is not set', () => {
70
+ const event = { source: contentWindow, origin: host } as unknown as MessageEvent;
71
+ expect(
72
+ isMessageFromIframe(event, null as unknown as HTMLIFrameElement, host),
73
+ ).toBe(false);
74
+ });
75
+ });
76
+
77
+ describe('postMessageToIframe', () => {
78
+ const host = 'https://ts.example.com';
79
+ const message = { type: 'someEvent', data: { a: 1 } };
80
+
81
+ it('posts with no transferables when no channel is provided', () => {
82
+ const postMessage = jest.fn();
83
+ const iFrame = { contentWindow: { postMessage } } as unknown as HTMLIFrameElement;
84
+
85
+ postMessageToIframe(iFrame, message, host);
86
+
87
+ expect(postMessage).toHaveBeenCalledWith(message, host, []);
88
+ });
89
+
90
+ it('transfers the channel port2 when a channel is provided', () => {
91
+ const postMessage = jest.fn();
92
+ const iFrame = { contentWindow: { postMessage } } as unknown as HTMLIFrameElement;
93
+ const port2 = {} as MessagePort;
94
+ const channel = { port2 } as MessageChannel;
95
+
96
+ postMessageToIframe(iFrame, message, host, channel);
97
+
98
+ expect(postMessage).toHaveBeenCalledWith(message, host, [port2]);
99
+ });
100
+
101
+ it('is a no-op when the iframe has no contentWindow', () => {
102
+ const iFrame = { contentWindow: null } as unknown as HTMLIFrameElement;
103
+ expect(() => postMessageToIframe(iFrame, message, host)).not.toThrow();
104
+ });
105
+ });
106
+
107
+ describe('sendMessageWithResponse', () => {
108
+ const host = 'https://ts.example.com';
109
+ const message = { type: 'trigger', data: {} };
110
+ let port1: { onmessage: ((e: { data: any }) => void) | null; close: jest.Mock };
111
+ let port2: MessagePort;
112
+ let postMessage: jest.Mock;
113
+ let iFrame: HTMLIFrameElement;
114
+ let OriginalMessageChannel: typeof MessageChannel;
115
+
116
+ beforeEach(() => {
117
+ port1 = { onmessage: null, close: jest.fn() };
118
+ port2 = {} as MessagePort;
119
+ OriginalMessageChannel = global.MessageChannel;
120
+ (global as any).MessageChannel = jest.fn(() => ({ port1, port2 }));
121
+ postMessage = jest.fn();
122
+ iFrame = { contentWindow: { postMessage } } as unknown as HTMLIFrameElement;
123
+ });
124
+
125
+ afterEach(() => {
126
+ global.MessageChannel = OriginalMessageChannel;
127
+ });
128
+
129
+ it('exposes the default response timeout', () => {
130
+ expect(MESSAGE_RESPONSE_TIMEOUT).toBe(30000);
131
+ });
132
+
133
+ it('posts the message with the channel port and resolves with the response', async () => {
134
+ const promise = sendMessageWithResponse(iFrame, message, host);
135
+
136
+ expect(postMessage).toHaveBeenCalledWith(message, host, [port2]);
137
+
138
+ // Simulate the app posting a response back on the channel — the
139
+ // handler resolves with the event's `data` payload.
140
+ port1.onmessage?.({ data: { value: 'ok' } });
141
+
142
+ await expect(promise).resolves.toEqual({ value: 'ok' });
143
+ expect(port1.close).toHaveBeenCalled();
144
+ });
145
+
146
+ it('rejects when the response carries a top-level error', async () => {
147
+ const promise = sendMessageWithResponse(iFrame, message, host);
148
+ port1.onmessage?.({ data: { error: 'boom' } });
149
+ await expect(promise).rejects.toBe('boom');
150
+ });
151
+
152
+ it('rejects when the error is nested under data', async () => {
153
+ const promise = sendMessageWithResponse(iFrame, message, host);
154
+ port1.onmessage?.({ data: { data: { error: 'nested-boom' } } });
155
+ await expect(promise).rejects.toBe('nested-boom');
156
+ });
157
+
158
+ it('resolves with an empty response when no data is returned', async () => {
159
+ const promise = sendMessageWithResponse(iFrame, message, host);
160
+ port1.onmessage?.({ data: undefined });
161
+ await expect(promise).resolves.toBeUndefined();
162
+ });
163
+
164
+ it('resolves with a timeout Error when no response arrives in time', async () => {
165
+ jest.useFakeTimers();
166
+ const promise = sendMessageWithResponse(iFrame, message, host, 100);
167
+
168
+ jest.advanceTimersByTime(100);
169
+
170
+ const result = await promise;
171
+ expect(result).toBeInstanceOf(Error);
172
+ expect((result as Error).message).toBe(ERROR_MESSAGE.TRIGGER_TIMED_OUT);
173
+ expect(port1.close).toHaveBeenCalled();
174
+ jest.useRealTimers();
175
+ });
176
+ });
177
+ });