@thoughtspot/visual-embed-sdk 1.52.1 → 1.52.2-beta.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 (217) hide show
  1. package/cjs/package.json +10 -3
  2. package/cjs/src/contracts/host-event-contracts.d.ts +49 -11
  3. package/cjs/src/contracts/host-event-contracts.d.ts.map +1 -1
  4. package/cjs/src/embed/app.d.ts +4 -3
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js.map +1 -1
  7. package/cjs/src/embed/app.spec.js +10 -0
  8. package/cjs/src/embed/app.spec.js.map +1 -1
  9. package/cjs/src/embed/auto-frame-renderer.spec.js +24 -13
  10. package/cjs/src/embed/auto-frame-renderer.spec.js.map +1 -1
  11. package/cjs/src/embed/conversation.d.ts +1 -0
  12. package/cjs/src/embed/conversation.d.ts.map +1 -1
  13. package/cjs/src/embed/conversation.js +3 -3
  14. package/cjs/src/embed/conversation.js.map +1 -1
  15. package/cjs/src/embed/conversation.spec.js +35 -24
  16. package/cjs/src/embed/conversation.spec.js.map +1 -1
  17. package/cjs/src/embed/embedConfig.spec.js +2 -1
  18. package/cjs/src/embed/embedConfig.spec.js.map +1 -1
  19. package/cjs/src/embed/hostEventClient/host-event-client.d.ts +2 -2
  20. package/cjs/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  21. package/cjs/src/embed/hostEventClient/host-event-client.js +16 -10
  22. package/cjs/src/embed/hostEventClient/host-event-client.js.map +1 -1
  23. package/cjs/src/embed/hostEventClient/host-event-client.spec.js +14 -8
  24. package/cjs/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  25. package/cjs/src/embed/hostEventClient/utils.d.ts +9 -0
  26. package/cjs/src/embed/hostEventClient/utils.d.ts.map +1 -1
  27. package/cjs/src/embed/hostEventClient/utils.js +49 -3
  28. package/cjs/src/embed/hostEventClient/utils.js.map +1 -1
  29. package/cjs/src/embed/hostEventClient/utils.spec.js +151 -0
  30. package/cjs/src/embed/hostEventClient/utils.spec.js.map +1 -1
  31. package/cjs/src/embed/liveboard.d.ts +4 -3
  32. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  33. package/cjs/src/embed/liveboard.js +17 -12
  34. package/cjs/src/embed/liveboard.js.map +1 -1
  35. package/cjs/src/embed/liveboard.spec.js +2 -1
  36. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  37. package/cjs/src/embed/spotter-utils.d.ts +8 -1
  38. package/cjs/src/embed/spotter-utils.d.ts.map +1 -1
  39. package/cjs/src/embed/spotter-utils.js +14 -1
  40. package/cjs/src/embed/spotter-utils.js.map +1 -1
  41. package/cjs/src/embed/spotter-utils.spec.js +26 -0
  42. package/cjs/src/embed/spotter-utils.spec.js.map +1 -1
  43. package/cjs/src/embed/ts-embed.d.ts +35 -10
  44. package/cjs/src/embed/ts-embed.d.ts.map +1 -1
  45. package/cjs/src/embed/ts-embed.js +108 -73
  46. package/cjs/src/embed/ts-embed.js.map +1 -1
  47. package/cjs/src/embed/ts-embed.spec.d.ts.map +1 -1
  48. package/cjs/src/embed/ts-embed.spec.js +203 -30
  49. package/cjs/src/embed/ts-embed.spec.js.map +1 -1
  50. package/cjs/src/errors.js +1 -1
  51. package/cjs/src/errors.js.map +1 -1
  52. package/cjs/src/index.d.ts +2 -1
  53. package/cjs/src/index.d.ts.map +1 -1
  54. package/cjs/src/index.js +3 -1
  55. package/cjs/src/index.js.map +1 -1
  56. package/cjs/src/react/all-types-export.d.ts +1 -1
  57. package/cjs/src/react/all-types-export.d.ts.map +1 -1
  58. package/cjs/src/react/all-types-export.js +2 -1
  59. package/cjs/src/react/all-types-export.js.map +1 -1
  60. package/cjs/src/types.d.ts +45 -22
  61. package/cjs/src/types.d.ts.map +1 -1
  62. package/cjs/src/types.js +44 -21
  63. package/cjs/src/types.js.map +1 -1
  64. package/cjs/src/utils/authService/tokenizedAuthService.spec.js +2 -1
  65. package/cjs/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  66. package/cjs/src/utils/custom-actions.spec.js +4 -2
  67. package/cjs/src/utils/custom-actions.spec.js.map +1 -1
  68. package/cjs/src/utils/filterConverter.d.ts +106 -0
  69. package/cjs/src/utils/filterConverter.d.ts.map +1 -0
  70. package/cjs/src/utils/filterConverter.js +215 -0
  71. package/cjs/src/utils/filterConverter.js.map +1 -0
  72. package/cjs/src/utils/filterConverter.spec.d.ts +2 -0
  73. package/cjs/src/utils/filterConverter.spec.d.ts.map +1 -0
  74. package/cjs/src/utils/filterConverter.spec.js +709 -0
  75. package/cjs/src/utils/filterConverter.spec.js.map +1 -0
  76. package/cjs/src/utils/processData.spec.js +2 -1
  77. package/cjs/src/utils/processData.spec.js.map +1 -1
  78. package/dist/{index-C1pmYb5H.js → index-BrJMbt8r.js} +1 -1
  79. package/dist/src/contracts/host-event-contracts.d.ts +49 -11
  80. package/dist/src/contracts/host-event-contracts.d.ts.map +1 -1
  81. package/dist/src/embed/app.d.ts +4 -3
  82. package/dist/src/embed/app.d.ts.map +1 -1
  83. package/dist/src/embed/conversation.d.ts +1 -0
  84. package/dist/src/embed/conversation.d.ts.map +1 -1
  85. package/dist/src/embed/hostEventClient/host-event-client.d.ts +2 -2
  86. package/dist/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  87. package/dist/src/embed/hostEventClient/utils.d.ts +9 -0
  88. package/dist/src/embed/hostEventClient/utils.d.ts.map +1 -1
  89. package/dist/src/embed/liveboard.d.ts +4 -3
  90. package/dist/src/embed/liveboard.d.ts.map +1 -1
  91. package/dist/src/embed/spotter-utils.d.ts +8 -1
  92. package/dist/src/embed/spotter-utils.d.ts.map +1 -1
  93. package/dist/src/embed/ts-embed.d.ts +35 -10
  94. package/dist/src/embed/ts-embed.d.ts.map +1 -1
  95. package/dist/src/embed/ts-embed.spec.d.ts.map +1 -1
  96. package/dist/src/index.d.ts +2 -1
  97. package/dist/src/index.d.ts.map +1 -1
  98. package/dist/src/react/all-types-export.d.ts +1 -1
  99. package/dist/src/react/all-types-export.d.ts.map +1 -1
  100. package/dist/src/types.d.ts +45 -22
  101. package/dist/src/types.d.ts.map +1 -1
  102. package/dist/src/utils/filterConverter.d.ts +106 -0
  103. package/dist/src/utils/filterConverter.d.ts.map +1 -0
  104. package/dist/src/utils/filterConverter.spec.d.ts +2 -0
  105. package/dist/src/utils/filterConverter.spec.d.ts.map +1 -0
  106. package/dist/tsembed-react.es.js +252 -126
  107. package/dist/tsembed-react.js +251 -125
  108. package/dist/tsembed.es.js +460 -127
  109. package/dist/tsembed.js +459 -125
  110. package/dist/visual-embed-sdk-react-full.d.ts +233 -49
  111. package/dist/visual-embed-sdk-react.d.ts +139 -49
  112. package/dist/visual-embed-sdk.d.ts +233 -49
  113. package/lib/package.json +10 -3
  114. package/lib/src/contracts/host-event-contracts.d.ts +49 -11
  115. package/lib/src/contracts/host-event-contracts.d.ts.map +1 -1
  116. package/lib/src/embed/app.d.ts +4 -3
  117. package/lib/src/embed/app.d.ts.map +1 -1
  118. package/lib/src/embed/app.js.map +1 -1
  119. package/lib/src/embed/app.spec.js +10 -0
  120. package/lib/src/embed/app.spec.js.map +1 -1
  121. package/lib/src/embed/auto-frame-renderer.spec.js +24 -13
  122. package/lib/src/embed/auto-frame-renderer.spec.js.map +1 -1
  123. package/lib/src/embed/conversation.d.ts +1 -0
  124. package/lib/src/embed/conversation.d.ts.map +1 -1
  125. package/lib/src/embed/conversation.js +4 -4
  126. package/lib/src/embed/conversation.js.map +1 -1
  127. package/lib/src/embed/conversation.spec.js +35 -24
  128. package/lib/src/embed/conversation.spec.js.map +1 -1
  129. package/lib/src/embed/embedConfig.spec.js +2 -1
  130. package/lib/src/embed/embedConfig.spec.js.map +1 -1
  131. package/lib/src/embed/hostEventClient/host-event-client.d.ts +2 -2
  132. package/lib/src/embed/hostEventClient/host-event-client.d.ts.map +1 -1
  133. package/lib/src/embed/hostEventClient/host-event-client.js +17 -11
  134. package/lib/src/embed/hostEventClient/host-event-client.js.map +1 -1
  135. package/lib/src/embed/hostEventClient/host-event-client.spec.js +14 -8
  136. package/lib/src/embed/hostEventClient/host-event-client.spec.js.map +1 -1
  137. package/lib/src/embed/hostEventClient/utils.d.ts +9 -0
  138. package/lib/src/embed/hostEventClient/utils.d.ts.map +1 -1
  139. package/lib/src/embed/hostEventClient/utils.js +47 -2
  140. package/lib/src/embed/hostEventClient/utils.js.map +1 -1
  141. package/lib/src/embed/hostEventClient/utils.spec.js +152 -1
  142. package/lib/src/embed/hostEventClient/utils.spec.js.map +1 -1
  143. package/lib/src/embed/liveboard.d.ts +4 -3
  144. package/lib/src/embed/liveboard.d.ts.map +1 -1
  145. package/lib/src/embed/liveboard.js +17 -12
  146. package/lib/src/embed/liveboard.js.map +1 -1
  147. package/lib/src/embed/liveboard.spec.js +2 -1
  148. package/lib/src/embed/liveboard.spec.js.map +1 -1
  149. package/lib/src/embed/spotter-utils.d.ts +8 -1
  150. package/lib/src/embed/spotter-utils.d.ts.map +1 -1
  151. package/lib/src/embed/spotter-utils.js +12 -0
  152. package/lib/src/embed/spotter-utils.js.map +1 -1
  153. package/lib/src/embed/spotter-utils.spec.js +27 -1
  154. package/lib/src/embed/spotter-utils.spec.js.map +1 -1
  155. package/lib/src/embed/ts-embed.d.ts +35 -10
  156. package/lib/src/embed/ts-embed.d.ts.map +1 -1
  157. package/lib/src/embed/ts-embed.js +109 -74
  158. package/lib/src/embed/ts-embed.js.map +1 -1
  159. package/lib/src/embed/ts-embed.spec.d.ts.map +1 -1
  160. package/lib/src/embed/ts-embed.spec.js +203 -30
  161. package/lib/src/embed/ts-embed.spec.js.map +1 -1
  162. package/lib/src/errors.js +1 -1
  163. package/lib/src/errors.js.map +1 -1
  164. package/lib/src/index.d.ts +2 -1
  165. package/lib/src/index.d.ts.map +1 -1
  166. package/lib/src/index.js +2 -1
  167. package/lib/src/index.js.map +1 -1
  168. package/lib/src/react/all-types-export.d.ts +1 -1
  169. package/lib/src/react/all-types-export.d.ts.map +1 -1
  170. package/lib/src/react/all-types-export.js +1 -1
  171. package/lib/src/react/all-types-export.js.map +1 -1
  172. package/lib/src/types.d.ts +45 -22
  173. package/lib/src/types.d.ts.map +1 -1
  174. package/lib/src/types.js +44 -21
  175. package/lib/src/types.js.map +1 -1
  176. package/lib/src/utils/authService/tokenizedAuthService.spec.js +2 -1
  177. package/lib/src/utils/authService/tokenizedAuthService.spec.js.map +1 -1
  178. package/lib/src/utils/custom-actions.spec.js +4 -2
  179. package/lib/src/utils/custom-actions.spec.js.map +1 -1
  180. package/lib/src/utils/filterConverter.d.ts +106 -0
  181. package/lib/src/utils/filterConverter.d.ts.map +1 -0
  182. package/lib/src/utils/filterConverter.js +210 -0
  183. package/lib/src/utils/filterConverter.js.map +1 -0
  184. package/lib/src/utils/filterConverter.spec.d.ts +2 -0
  185. package/lib/src/utils/filterConverter.spec.d.ts.map +1 -0
  186. package/lib/src/utils/filterConverter.spec.js +707 -0
  187. package/lib/src/utils/filterConverter.spec.js.map +1 -0
  188. package/lib/src/utils/processData.spec.js +2 -1
  189. package/lib/src/utils/processData.spec.js.map +1 -1
  190. package/lib/src/visual-embed-sdk.d.ts +233 -49
  191. package/package.json +10 -3
  192. package/src/contracts/host-event-contracts.ts +55 -11
  193. package/src/embed/app.spec.ts +15 -0
  194. package/src/embed/app.ts +4 -3
  195. package/src/embed/auto-frame-renderer.spec.ts +24 -13
  196. package/src/embed/conversation.spec.ts +38 -27
  197. package/src/embed/conversation.ts +4 -4
  198. package/src/embed/embedConfig.spec.ts +2 -1
  199. package/src/embed/hostEventClient/host-event-client.spec.ts +14 -8
  200. package/src/embed/hostEventClient/host-event-client.ts +18 -10
  201. package/src/embed/hostEventClient/utils.spec.ts +176 -1
  202. package/src/embed/hostEventClient/utils.ts +51 -2
  203. package/src/embed/liveboard.spec.ts +4 -3
  204. package/src/embed/liveboard.ts +21 -15
  205. package/src/embed/spotter-utils.spec.ts +29 -0
  206. package/src/embed/spotter-utils.ts +16 -0
  207. package/src/embed/ts-embed.spec.ts +245 -30
  208. package/src/embed/ts-embed.ts +107 -74
  209. package/src/errors.ts +1 -1
  210. package/src/index.ts +10 -0
  211. package/src/react/all-types-export.ts +4 -0
  212. package/src/types.ts +44 -21
  213. package/src/utils/authService/tokenizedAuthService.spec.ts +2 -1
  214. package/src/utils/custom-actions.spec.ts +4 -2
  215. package/src/utils/filterConverter.spec.ts +801 -0
  216. package/src/utils/filterConverter.ts +304 -0
  217. package/src/utils/processData.spec.ts +2 -1
@@ -168,23 +168,67 @@ export interface UpdateCrossFilterRequest {
168
168
  }
169
169
 
170
170
  /**
171
- * A single filter entry for {@link HostEvent.UpdateFilters}. Supports both
172
- * the current (columnName/operator) and legacy (column/oper) field names.
171
+ * The column to filter on. Supply `columnName`. A filter with no column is
172
+ * rejected at runtime, and passing both spellings silently discards the
173
+ * `column` one.
174
+ *
175
+ * When several columns share a name, qualify it as
176
+ * `WORKSHEET_NAME::COLUMN_NAME`, for example
177
+ * `"(Sample) Retail - Apparel::city"`.
173
178
  */
174
- export interface HostFilterUpdate {
175
- columnName?: string;
179
+ export type HostFilterUpdateColumn =
180
+ | {
181
+ /**
182
+ * Name of the column to filter on, optionally qualified as
183
+ * `WORKSHEET_NAME::COLUMN_NAME`.
184
+ */
185
+ columnName: string;
186
+ column?: never;
187
+ }
188
+ | {
189
+ /**
190
+ * @deprecated Use `columnName`, which matches {@link RuntimeFilter} and
191
+ * the rest of the SDK. Still accepted, but not alongside `columnName`.
192
+ */
193
+ column: string;
194
+ columnName?: never;
195
+ };
196
+
197
+ /**
198
+ * The filter operator. Supply `operator`. A filter with no operator is
199
+ * rejected at runtime, and passing both spellings silently discards the
200
+ * `oper` one.
201
+ */
202
+ export type HostFilterUpdateOperator =
203
+ | {
204
+ /** Filter operator, for example EQ, IN, CONTAINS. */
205
+ operator: string;
206
+ oper?: never;
207
+ }
208
+ | {
209
+ /**
210
+ * @deprecated Use `operator`, which matches {@link RuntimeFilter} and
211
+ * the rest of the SDK. Still accepted, but not alongside `operator`.
212
+ */
213
+ oper: string;
214
+ operator?: never;
215
+ };
216
+
217
+ /**
218
+ * A single filter entry for {@link HostEvent.UpdateFilters}.
219
+ *
220
+ * Use `columnName` and `operator` - the spelling used by
221
+ * {@link RuntimeFilter} and by the payload
222
+ * `convertFilterChangedToUpdateFiltersPayload` produces.
223
+ */
224
+ export type HostFilterUpdate = HostFilterUpdateColumn & HostFilterUpdateOperator & {
176
225
  columnId?: string;
177
- operator?: string;
178
- values: Array<string | number | boolean>;
226
+ values: Array<string | number | boolean | bigint>;
179
227
  type?: string;
180
228
  datePeriod?: string;
181
229
  negate?: boolean;
182
- /** Legacy field name for columnName. */
183
- column?: string;
184
- /** Legacy field name for operator. */
185
- oper?: string;
186
230
  applicability?: Applicability;
187
- }
231
+ };
188
232
 
189
233
  /**
190
234
  * Request payload for {@link HostEvent.UpdateFilters} (singular or plural
@@ -375,6 +375,21 @@ describe('App embed tests', () => {
375
375
  });
376
376
  });
377
377
 
378
+ test('should hide the Customize homepage button via hiddenActions', async () => {
379
+ const appEmbed = new AppEmbed(getRootEl(), {
380
+ ...defaultViewConfig,
381
+ hiddenActions: [Action.CustomizeHomepage],
382
+ } as AppViewConfig);
383
+
384
+ appEmbed.render();
385
+ await executeAfterWait(() => {
386
+ expectUrlMatchesWithParams(
387
+ getIFrameSrc(),
388
+ `http://${thoughtSpotHost}/?embedApp=true&primaryNavHidden=true&profileAndHelpInNavBarHidden=false&hideHomepageLeftNav=false&navigationVersion=v3&homepageVersion=v3&${defaultParamsWithoutHiddenActions}&hideAction=[%22${Action.ReportError}%22,%22customizeHomepage%22]${defaultParamsPost}#/home`,
389
+ );
390
+ });
391
+ });
392
+
378
393
  test('should set enable2ColumnLayout to true in url', async () => {
379
394
  const appEmbed = new AppEmbed(getRootEl(), {
380
395
  ...defaultViewConfig,
package/src/embed/app.ts CHANGED
@@ -608,9 +608,10 @@ export interface AppViewConfig extends AllEmbedViewConfig, FullHeightViewConfig
608
608
  isPNGInScheduledEmailsEnabled?: boolean;
609
609
 
610
610
  /**
611
- * Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is rendered on a single page
612
- * following the exact UI layout, instead of splitting visualizations across multiple A4 pages.
613
- * This feature is GA from SDK version 1.51.0 and ThoughtSpot version 26.8.0.cl.
611
+ * Enables the 'what you see is what you get' PDF export for Liveboards. Each tab is
612
+ * rendered on a single page following the exact UI layout, instead of splitting
613
+ * visualizations across multiple A4 pages. This feature is GA from SDK version
614
+ * 1.51.0 and ThoughtSpot version 26.8.0.cl.
614
615
  *
615
616
  * Supported embed types: `AppEmbed`, `LiveboardEmbed`
616
617
  * @type {boolean}
@@ -45,7 +45,8 @@ describe('startAutoMCPFrameRenderer', () => {
45
45
  getEmbedBasePathSpy.mockRestore();
46
46
  });
47
47
 
48
- // ─── helpers ──────────────────────────────────────────────────────────────
48
+ // ─── helpers
49
+ // ──────────────────────────────────────────────────────────────
49
50
 
50
51
  /** Capture the src passed to renderIFrame for the first tsmcp iframe added */
51
52
  async function captureRenderedSrc(viewConfig: AutoMCPFrameRendererViewConfig = {}): Promise<string> {
@@ -96,7 +97,8 @@ describe('startAutoMCPFrameRenderer', () => {
96
97
  return mockPort.postMessage.mock.calls[0]?.[0];
97
98
  }
98
99
 
99
- // ─── MutationObserver setup ───────────────────────────────────────────────
100
+ // ─── MutationObserver setup
101
+ // ───────────────────────────────────────────────
100
102
 
101
103
  describe('MutationObserver setup', () => {
102
104
  test('should return a MutationObserver', () => {
@@ -117,7 +119,8 @@ describe('startAutoMCPFrameRenderer', () => {
117
119
  });
118
120
  });
119
121
 
120
- // ─── iframe detection via tsmcp param ─────────────────────────────────────
122
+ // ─── iframe detection via tsmcp param
123
+ // ─────────────────────────────────────
121
124
 
122
125
  describe('iframe detection via tsmcp param', () => {
123
126
  test('should process directly-added iframes with tsmcp=true', async () => {
@@ -209,7 +212,8 @@ describe('startAutoMCPFrameRenderer', () => {
209
212
  });
210
213
  });
211
214
 
212
- // ─── handleInsertionIntoDOM override ──────────────────────────────────────
215
+ // ─── handleInsertionIntoDOM override
216
+ // ──────────────────────────────────────
213
217
 
214
218
  describe('handleInsertionIntoDOM override', () => {
215
219
  test('should replace the original iframe when renderIFrame inserts DOM', async () => {
@@ -233,7 +237,8 @@ describe('startAutoMCPFrameRenderer', () => {
233
237
  });
234
238
  });
235
239
 
236
- // ─── URL params forwarding ────────────────────────────────────────────────
240
+ // ─── URL params forwarding
241
+ // ────────────────────────────────────────────────
237
242
 
238
243
  describe('URL params forwarding', () => {
239
244
  test('should accept empty viewConfig', () => {
@@ -394,7 +399,8 @@ describe('startAutoMCPFrameRenderer', () => {
394
399
  });
395
400
  });
396
401
 
397
- // ─── frameParams forwarding ───────────────────────────────────────────────
402
+ // ─── frameParams forwarding
403
+ // ───────────────────────────────────────────────
398
404
 
399
405
  describe('frameParams forwarding', () => {
400
406
  test('frameParams.height and .width applied to the replacement iframe element', async () => {
@@ -441,7 +447,8 @@ describe('startAutoMCPFrameRenderer', () => {
441
447
  });
442
448
  });
443
449
 
444
- // ─── APP_INIT postMessage params forwarding ───────────────────────────────
450
+ // ─── APP_INIT postMessage params forwarding
451
+ // ───────────────────────────────
445
452
  //
446
453
  // These params are not in the iframe src URL — they travel via the APP_INIT
447
454
  // postMessage channel. AutoFrameRenderer inherits the TsEmbed APP_INIT
@@ -497,9 +504,10 @@ describe('startAutoMCPFrameRenderer', () => {
497
504
  });
498
505
 
499
506
  test('interceptUrls forwarded via APP_INIT', async () => {
500
- // The SDK expands InterceptedApiType enum values into resolved prism endpoint URLs.
501
- // Assert that the interceptUrls array is non-empty (i.e. the config was forwarded
502
- // and processed) rather than checking the resolved strings directly.
507
+ // The SDK expands InterceptedApiType enum values into resolved
508
+ // prism endpoint URLs. Assert that the interceptUrls array is
509
+ // non-empty (i.e. the config was forwarded and processed) rather
510
+ // than checking the resolved strings directly.
503
511
  const response = await getAppInitResponse({
504
512
  interceptUrls: [InterceptedApiType.AnswerData],
505
513
  });
@@ -513,7 +521,8 @@ describe('startAutoMCPFrameRenderer', () => {
513
521
  });
514
522
  });
515
523
 
516
- // ─── getMCPIframeSrc URL construction ─────────────────────────────────────
524
+ // ─── getMCPIframeSrc URL construction
525
+ // ─────────────────────────────────────
517
526
 
518
527
  describe('getMCPIframeSrc URL construction', () => {
519
528
  test('should strip tsmcp param and merge embed params into rendered src', async () => {
@@ -569,7 +578,8 @@ describe('startAutoMCPFrameRenderer', () => {
569
578
  document.body.appendChild(iframe);
570
579
  await new Promise((r) => setTimeout(r, 50));
571
580
 
572
- // At minimum the base embed params (hostAppUrl, sdkVersion, etc.) are always present
581
+ // At minimum the base embed params (hostAppUrl, sdkVersion, etc.)
582
+ // are always present
573
583
  expect(capturedSrc).toMatch(/\?[^#]+#/);
574
584
  observer.disconnect();
575
585
  });
@@ -594,7 +604,8 @@ describe('startAutoMCPFrameRenderer', () => {
594
604
  });
595
605
  });
596
606
 
597
- // ─── URL serialization parity with normal embeds ──────────────────────────
607
+ // ─── URL serialization parity with normal embeds
608
+ // ──────────────────────────
598
609
  //
599
610
  // These tests verify that array-typed params (hideAction, disableAction,
600
611
  // visibleAction) are serialized identically by startAutoMCPFrameRenderer
@@ -537,7 +537,7 @@ describe('ConversationEmbed', () => {
537
537
  );
538
538
  });
539
539
 
540
- it('should render the conversation embed with spotterAnalystConfig.analystId in the url', async () => {
540
+ it('should not add analystId to the url, it is sent over APP_INIT instead', async () => {
541
541
  const viewConfig: SpotterEmbedViewConfig = {
542
542
  worksheetId: 'worksheetId',
543
543
  searchOptions: {
@@ -549,37 +549,13 @@ describe('ConversationEmbed', () => {
549
549
  };
550
550
  const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
551
551
  await conversationEmbed.render();
552
+ expect(getIFrameSrc()).not.toContain('analystId');
552
553
  expectUrlMatchesWithParams(
553
554
  getIFrameSrc(),
554
- `http://${thoughtSpotHost}/v2/?${defaultParams}&isSpotterExperienceEnabled=true&analystId=analyst-id-1234#/embed/insights/conv-assist?worksheet=worksheetId&query=searchQuery`,
555
+ `http://${thoughtSpotHost}/v2/?${defaultParams}&isSpotterExperienceEnabled=true#/embed/insights/conv-assist?worksheet=worksheetId&query=searchQuery`,
555
556
  );
556
557
  });
557
558
 
558
- it('should not add analystId to the url when spotterAnalystConfig is not provided', async () => {
559
- const viewConfig: SpotterEmbedViewConfig = {
560
- worksheetId: 'worksheetId',
561
- searchOptions: {
562
- searchQuery: 'searchQuery',
563
- },
564
- };
565
- const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
566
- await conversationEmbed.render();
567
- expect(getIFrameSrc()).not.toContain('analystId');
568
- });
569
-
570
- it('should not add analystId to the url when spotterAnalystConfig is empty', async () => {
571
- const viewConfig: SpotterEmbedViewConfig = {
572
- worksheetId: 'worksheetId',
573
- searchOptions: {
574
- searchQuery: 'searchQuery',
575
- },
576
- spotterAnalystConfig: {},
577
- };
578
- const conversationEmbed = new SpotterEmbed(getRootEl(), viewConfig);
579
- await conversationEmbed.render();
580
- expect(getIFrameSrc()).not.toContain('analystId');
581
- });
582
-
583
559
  describe('spotter chat hiddenActions', () => {
584
560
  it.each([
585
561
  ['SpotterChatConnectorResources', Action.SpotterChatConnectorResources],
@@ -1057,4 +1033,39 @@ describe('SpotterEmbed APP_INIT starterPrompts', () => {
1057
1033
  quick: { questions: [{ label: 'Q1', prompt: 'P1' }] },
1058
1034
  });
1059
1035
  });
1036
+
1037
+ it('should include spotterAnalystConfig in embedParams when analystId is provided', async () => {
1038
+ const response = await getAppInitResponse({
1039
+ worksheetId: 'ws1',
1040
+ spotterAnalystConfig: { analystId: 'analyst-id-1234' },
1041
+ });
1042
+ expect(response.data.embedParams.spotterAnalystConfig).toEqual({
1043
+ analystId: 'analyst-id-1234',
1044
+ });
1045
+ });
1046
+
1047
+ it('should include spotterAnalystConfig alongside the other spotter embedParams', async () => {
1048
+ const response = await getAppInitResponse({
1049
+ worksheetId: 'ws1',
1050
+ spotterSidebarConfig: { enablePastConversationsSidebar: true },
1051
+ spotterAnalystConfig: { analystId: 'analyst-id-1234' },
1052
+ });
1053
+ expect(response.data.embedParams.spotterSidebarConfig.enablePastConversationsSidebar).toBe(true);
1054
+ expect(response.data.embedParams.spotterAnalystConfig).toEqual({
1055
+ analystId: 'analyst-id-1234',
1056
+ });
1057
+ });
1058
+
1059
+ it('should not include spotterAnalystConfig in embedParams when not configured', async () => {
1060
+ const response = await getAppInitResponse({ worksheetId: 'ws1' });
1061
+ expect(response.data.embedParams?.spotterAnalystConfig).toBeUndefined();
1062
+ });
1063
+
1064
+ it('should not include spotterAnalystConfig in embedParams when analystId is absent', async () => {
1065
+ const response = await getAppInitResponse({
1066
+ worksheetId: 'ws1',
1067
+ spotterAnalystConfig: {},
1068
+ });
1069
+ expect(response.data.embedParams?.spotterAnalystConfig).toBeUndefined();
1070
+ });
1060
1071
  });
@@ -1,6 +1,6 @@
1
1
  import { Param, BaseViewConfig, RuntimeFilter, RuntimeParameter, ErrorDetailsTypes, EmbedErrorCodes, DefaultAppInitData, VisualizationOverrides, SpotterFileUploadFileTypes } from '../types';
2
2
  import { TsEmbed } from './ts-embed';
3
- import { buildSpotterSidebarAppInitData, buildSpotterShareConversationAppInitData, buildStarterPromptsAppInitData } from './spotter-utils';
3
+ import { buildSpotterSidebarAppInitData, buildSpotterShareConversationAppInitData, buildStarterPromptsAppInitData, buildSpotterAnalystAppInitData } from './spotter-utils';
4
4
  import { getQueryParamString, getFilterQuery, getRuntimeParameters, setParamIfDefined } from '../utils';
5
5
 
6
6
  /**
@@ -801,6 +801,7 @@ export interface SpotterAppInitData extends DefaultAppInitData {
801
801
  spotterShareConversationConfig?: SpotterShareConversationConfig;
802
802
  visualOverridesParams?: VisualizationOverrides | null;
803
803
  starterPrompts?: StarterPromptsConfig;
804
+ spotterAnalystConfig?: SpotterAnalystConfig;
804
805
  };
805
806
  }
806
807
 
@@ -850,7 +851,8 @@ export class SpotterEmbed extends TsEmbed {
850
851
  this.handleError.bind(this),
851
852
  );
852
853
  const shareInitData = buildSpotterShareConversationAppInitData(sidebarInitData, this.viewConfig);
853
- return buildStarterPromptsAppInitData(shareInitData, this.viewConfig);
854
+ const starterPromptsInitData = buildStarterPromptsAppInitData(shareInitData, this.viewConfig);
855
+ return buildSpotterAnalystAppInitData(starterPromptsInitData, this.viewConfig);
854
856
  }
855
857
 
856
858
  protected getEmbedParamsObject() {
@@ -871,7 +873,6 @@ export class SpotterEmbed extends TsEmbed {
871
873
  defaultQueryMode,
872
874
  enableStopAnswerGenerationEmbed,
873
875
  spotterChatConfig,
874
- spotterAnalystConfig,
875
876
  } = this.viewConfig;
876
877
 
877
878
  const queryParams = this.getBaseQueryParams();
@@ -887,7 +888,6 @@ export class SpotterEmbed extends TsEmbed {
887
888
  setParamIfDefined(queryParams, Param.ShowSpotterRadiance, showSpotterRadiance, true);
888
889
  setParamIfDefined(queryParams, Param.DefaultQueryMode, defaultQueryMode);
889
890
  setParamIfDefined(queryParams, Param.EnableStopAnswerGenerationEmbed, enableStopAnswerGenerationEmbed, true);
890
- setParamIfDefined(queryParams, Param.AnalystId, spotterAnalystConfig?.analystId);
891
891
 
892
892
  // Handle spotterChatConfig params
893
893
  if (spotterChatConfig) {
@@ -3,7 +3,8 @@ import { storeValueInWindow, resetValueFromWindow } from '../utils';
3
3
 
4
4
  describe('embedConfig', () => {
5
5
  beforeEach(() => {
6
- // Ensure the SDK window namespace exists before resetting individual keys
6
+ // Ensure the SDK window namespace exists before resetting individual
7
+ // keys
7
8
  storeValueInWindow('embedConfig', undefined);
8
9
  resetValueFromWindow('embedConfig');
9
10
  });
@@ -979,11 +979,14 @@ describe('HostEventClient', () => {
979
979
  const { client } = createHostEventClient();
980
980
  mockProcessTrigger.mockReset();
981
981
 
982
- // Flow per triggerHostEvent call when GetAvailableUIPassthroughs rejects:
983
- // 1. getAvailableUIPassthroughKeys → processTrigger(UIPassthrough, GetAvailableUIPassthroughs) → rejects → returns []
984
- // 2. keys=[] so getDataWithPassthroughFallback is called →
985
- // triggerUIPassthroughApi(GetAnswerSession) → processTrigger(UIPassthrough) → null → hostEventFallback called
986
- // 3. hostEventFallback → processTrigger(GetAnswerSession) → legacy result
982
+ // Flow per triggerHostEvent call when GetAvailableUIPassthroughs
983
+ // rejects: 1. getAvailableUIPassthroughKeys →
984
+ // processTrigger(UIPassthrough, GetAvailableUIPassthroughs) →
985
+ // rejects → returns [] 2. keys=[] so getDataWithPassthroughFallback
986
+ // is called → triggerUIPassthroughApi(GetAnswerSession) →
987
+ // processTrigger(UIPassthrough) → null → hostEventFallback called
988
+ // 3. hostEventFallback → processTrigger(GetAnswerSession) → legacy
989
+ // result
987
990
 
988
991
  // First triggerHostEvent: 3 calls
989
992
  mockProcessTrigger.mockRejectedValueOnce(new Error('network error')); // call 1
@@ -992,14 +995,16 @@ describe('HostEventClient', () => {
992
995
 
993
996
  await client.triggerHostEvent(HostEvent.GetAnswerSession, { vizId: '1' });
994
997
 
995
- // Second triggerHostEvent: cache was NOT set, so GetAvailableUIPassthroughs is retried: 3 more calls
998
+ // Second triggerHostEvent: cache was NOT set, so
999
+ // GetAvailableUIPassthroughs is retried: 3 more calls
996
1000
  mockProcessTrigger.mockRejectedValueOnce(new Error('network error')); // call 4
997
1001
  mockProcessTrigger.mockResolvedValueOnce(null); // call 5
998
1002
  mockProcessTrigger.mockResolvedValueOnce({ session: 'leg2' }); // call 6
999
1003
 
1000
1004
  await client.triggerHostEvent(HostEvent.GetAnswerSession, { vizId: '2' });
1001
1005
 
1002
- // 6 total calls: GetAvailableUIPassthroughs was called TWICE (cache not set after catch)
1006
+ // 6 total calls: GetAvailableUIPassthroughs was called TWICE
1007
+ // (cache not set after catch)
1003
1008
  expect(mockProcessTrigger).toHaveBeenCalledTimes(6);
1004
1009
  const availableCallsCount = mockProcessTrigger.mock.calls.filter(
1005
1010
  (call) => call[3]?.type === UIPassthroughEvent.GetAvailableUIPassthroughs,
@@ -1175,7 +1180,8 @@ describe('HostEventClient', () => {
1175
1180
  { numberOfTabs: number; orderedTabIds: string[] }
1176
1181
  > = true;
1177
1182
  const probeUnknownField = (r: TabsResponse) =>
1178
- // @ts-expect-error — field not on the GetTabs contract (was silent when `any`)
1183
+ // @ts-expect-error — field not on the GetTabs contract (was
1184
+ // silent when `any`)
1179
1185
  r.tabCount;
1180
1186
  void probeUnknownField;
1181
1187
 
@@ -3,6 +3,7 @@ import { processTrigger as processTriggerService } from '../../utils/processTrig
3
3
  import { getEmbedConfig } from '../embedConfig';
4
4
  import {
5
5
  isValidUpdateFiltersPayload,
6
+ resolveUpdateFiltersAliases,
6
7
  isValidUpdateParametersPayload,
7
8
  isValidDrillDownPayload,
8
9
  throwUpdateFiltersValidationError,
@@ -27,7 +28,8 @@ import {
27
28
  /**
28
29
  * Maps HostEvent to its corresponding UIPassthroughEvent.
29
30
  * Includes both custom-handler events (Pin, SaveAnswer, UpdateFilters, DrillDown)
30
- * and getter events (GetAnswerSession, GetFilters, etc.) that use getDataWithPassthroughFallback.
31
+ * and getter events (GetAnswerSession, GetFilters, etc.) that use
32
+ * getDataWithPassthroughFallback.
31
33
  */
32
34
  const PASSTHROUGH_MAP: Partial<Record<HostEvent, UIPassthroughEvent>> = {
33
35
  // Custom handlers (setters with special logic)
@@ -249,13 +251,17 @@ export class HostEventClient {
249
251
  }
250
252
 
251
253
  // The shared contract accepts both current (columnName/operator) and
252
- // legacy (column/oper) filter field names — as does the validation
253
- // above and the app at runtime. The UIPassthrough FilterUpdate type
254
- // still requires the legacy names; bridge until that contract is
255
- // audited.
254
+ // legacy (column/oper) filter field names, but only the legacy pair is
255
+ // understood downstream, so the payload is rewritten before it is
256
+ // forwarded — otherwise a columnName/operator filter passes validation
257
+ // here and is then silently ignored by the app. The cast bridges the
258
+ // UIPassthrough FilterUpdate type, which still declares only the legacy
259
+ // names.
260
+ const resolvedPayload = resolveUpdateFiltersAliases(payload);
261
+
256
262
  return this.handleHostEventWithParam(
257
263
  UIPassthroughEvent.UpdateFilters,
258
- payload as UIPassthroughRequest<UIPassthroughEvent.UpdateFilters>,
264
+ resolvedPayload as UIPassthroughRequest<UIPassthroughEvent.UpdateFilters>,
259
265
  context as ContextType,
260
266
  );
261
267
  }
@@ -268,7 +274,8 @@ export class HostEventClient {
268
274
  throwUpdateParametersValidationError();
269
275
  }
270
276
 
271
- // UpdateParameters has no UI passthrough contract; dispatch over the legacy channel
277
+ // UpdateParameters has no UI passthrough contract; dispatch over the
278
+ // legacy channel
272
279
  return this.hostEventFallback(HostEvent.UpdateParameters, payload, context);
273
280
  }
274
281
 
@@ -285,8 +292,8 @@ export class HostEventClient {
285
292
 
286
293
  /**
287
294
  * Dispatches a host event using the appropriate channel:
288
- * 1. If the embedded app supports UI passthrough for this event, use it (custom handler or getter).
289
- * 2. Otherwise fall back to the legacy host event channel.
295
+ * 1. If the embedded app supports UI passthrough for this event, use it (custom
296
+ * handler or getter). 2. Otherwise fall back to the legacy host event channel.
290
297
  *
291
298
  * @param hostEvent - The host event to trigger
292
299
  * @param payload - Optional payload for the event
@@ -304,7 +311,8 @@ export class HostEventClient {
304
311
  const customHandler = this.customHandlers[hostEvent];
305
312
  const passthroughEvent = PASSTHROUGH_MAP[hostEvent];
306
313
 
307
- // If embedded app supports passthrough but not this event, use legacy channel
314
+ // If embedded app supports passthrough but not this event, use legacy
315
+ // channel
308
316
  const keys = passthroughEvent ? await this.getAvailableUIPassthroughKeys(context as ContextType) : [];
309
317
  if (passthroughEvent && keys.length > 0 && !keys.includes(passthroughEvent)) {
310
318
  return this.hostEventFallback(hostEvent, payload, context) as any;