@thoughtspot/visual-embed-sdk 1.45.2 → 1.45.3-mcp.2

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 (250) hide show
  1. package/cjs/package.json +2 -2
  2. package/cjs/src/css-variables.d.ts +48 -0
  3. package/cjs/src/css-variables.d.ts.map +1 -1
  4. package/cjs/src/embed/app.d.ts +37 -0
  5. package/cjs/src/embed/app.d.ts.map +1 -1
  6. package/cjs/src/embed/app.js +37 -1
  7. package/cjs/src/embed/app.js.map +1 -1
  8. package/cjs/src/embed/app.spec.js +24 -0
  9. package/cjs/src/embed/app.spec.js.map +1 -1
  10. package/cjs/src/embed/auto-frame-renderer.d.ts +3 -0
  11. package/cjs/src/embed/auto-frame-renderer.d.ts.map +1 -0
  12. package/cjs/src/embed/auto-frame-renderer.js +70 -0
  13. package/cjs/src/embed/auto-frame-renderer.js.map +1 -0
  14. package/cjs/src/embed/base.d.ts.map +1 -1
  15. package/cjs/src/embed/base.js.map +1 -1
  16. package/cjs/src/embed/conversation.d.ts +128 -10
  17. package/cjs/src/embed/conversation.d.ts.map +1 -1
  18. package/cjs/src/embed/conversation.js +41 -18
  19. package/cjs/src/embed/conversation.js.map +1 -1
  20. package/cjs/src/embed/conversation.spec.js +96 -3
  21. package/cjs/src/embed/conversation.spec.js.map +1 -1
  22. package/cjs/src/embed/liveboard.d.ts +38 -1
  23. package/cjs/src/embed/liveboard.d.ts.map +1 -1
  24. package/cjs/src/embed/liveboard.js +38 -10
  25. package/cjs/src/embed/liveboard.js.map +1 -1
  26. package/cjs/src/embed/liveboard.spec.js +179 -7
  27. package/cjs/src/embed/liveboard.spec.js.map +1 -1
  28. package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  29. package/cjs/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  30. package/cjs/src/embed/searchEmbed-basic-auth.spec.js +104 -0
  31. package/cjs/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  32. package/cjs/src/errors.d.ts +1 -0
  33. package/cjs/src/errors.d.ts.map +1 -1
  34. package/cjs/src/errors.js +1 -0
  35. package/cjs/src/errors.js.map +1 -1
  36. package/cjs/src/index.d.ts +4 -3
  37. package/cjs/src/index.d.ts.map +1 -1
  38. package/cjs/src/index.js +3 -1
  39. package/cjs/src/index.js.map +1 -1
  40. package/cjs/src/types.d.ts +196 -41
  41. package/cjs/src/types.d.ts.map +1 -1
  42. package/cjs/src/types.js +185 -19
  43. package/cjs/src/types.js.map +1 -1
  44. package/cjs/src/utils/answerService/answerService.d.ts +34 -0
  45. package/cjs/src/utils/answerService/answerService.d.ts.map +1 -0
  46. package/cjs/src/utils/answerService/answerService.js +142 -0
  47. package/cjs/src/utils/answerService/answerService.js.map +1 -0
  48. package/cjs/src/utils/answerService/answerService.spec.d.ts +1 -0
  49. package/cjs/src/utils/answerService/answerService.spec.d.ts.map +1 -0
  50. package/cjs/src/utils/answerService/answerService.spec.js +1 -0
  51. package/cjs/src/utils/answerService/answerService.spec.js.map +1 -0
  52. package/cjs/src/utils/answerService/graphql-queries.d.ts +6 -0
  53. package/cjs/src/utils/answerService/graphql-queries.d.ts.map +1 -0
  54. package/cjs/src/utils/answerService/graphql-queries.js +123 -0
  55. package/cjs/src/utils/answerService/graphql-queries.js.map +1 -0
  56. package/cjs/src/utils/answerService.d.ts +10 -0
  57. package/cjs/src/utils/answerService.d.ts.map +1 -0
  58. package/cjs/src/utils/answerService.js +61 -0
  59. package/cjs/src/utils/answerService.js.map +1 -0
  60. package/cjs/src/utils/answerService.spec.d.ts +2 -0
  61. package/cjs/src/utils/answerService.spec.d.ts.map +1 -0
  62. package/cjs/src/utils/answerService.spec.js +31 -0
  63. package/cjs/src/utils/answerService.spec.js.map +1 -0
  64. package/cjs/src/utils/authService.d.ts +37 -0
  65. package/cjs/src/utils/authService.d.ts.map +1 -0
  66. package/cjs/src/utils/authService.js +106 -0
  67. package/cjs/src/utils/authService.js.map +1 -0
  68. package/cjs/src/utils/authService.spec.d.ts +2 -0
  69. package/cjs/src/utils/authService.spec.d.ts.map +1 -0
  70. package/cjs/src/utils/authService.spec.js +72 -0
  71. package/cjs/src/utils/authService.spec.js.map +1 -0
  72. package/cjs/src/utils/graphql/answerService/answer-queries.d.ts +1 -0
  73. package/cjs/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -1
  74. package/cjs/src/utils/graphql/answerService/answer-queries.js +23 -1
  75. package/cjs/src/utils/graphql/answerService/answer-queries.js.map +1 -1
  76. package/cjs/src/utils/graphql/answerService/answerService.d.ts +2 -1
  77. package/cjs/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  78. package/cjs/src/utils/graphql/answerService/answerService.js +9 -1
  79. package/cjs/src/utils/graphql/answerService/answerService.js.map +1 -1
  80. package/cjs/src/utils/graphql/answerService/answerService.spec.js +73 -0
  81. package/cjs/src/utils/graphql/answerService/answerService.spec.js.map +1 -1
  82. package/cjs/src/utils/graphql/answerService/graphql-queries.d.ts +5 -0
  83. package/cjs/src/utils/graphql/answerService/graphql-queries.d.ts.map +1 -0
  84. package/cjs/src/utils/graphql/answerService/graphql-queries.js +80 -0
  85. package/cjs/src/utils/graphql/answerService/graphql-queries.js.map +1 -0
  86. package/cjs/src/utils/graphql/conversationService/conversation-queries.d.ts +3 -0
  87. package/cjs/src/utils/graphql/conversationService/conversation-queries.d.ts.map +1 -0
  88. package/cjs/src/utils/graphql/conversationService/conversation-queries.js +318 -0
  89. package/cjs/src/utils/graphql/conversationService/conversation-queries.js.map +1 -0
  90. package/cjs/src/utils/graphql/conversationService/conversation-service.d.ts +12 -0
  91. package/cjs/src/utils/graphql/conversationService/conversation-service.d.ts.map +1 -0
  92. package/cjs/src/utils/graphql/conversationService/conversation-service.js +89 -0
  93. package/cjs/src/utils/graphql/conversationService/conversation-service.js.map +1 -0
  94. package/cjs/src/utils/graphql/spotterService/conversation-queries.d.ts +3 -0
  95. package/cjs/src/utils/graphql/spotterService/conversation-queries.d.ts.map +1 -0
  96. package/cjs/src/utils/graphql/spotterService/conversation-queries.js +318 -0
  97. package/cjs/src/utils/graphql/spotterService/conversation-queries.js.map +1 -0
  98. package/cjs/src/utils/graphql/spotterService/conversation-service.d.ts +12 -0
  99. package/cjs/src/utils/graphql/spotterService/conversation-service.d.ts.map +1 -0
  100. package/cjs/src/utils/graphql/spotterService/conversation-service.js +89 -0
  101. package/cjs/src/utils/graphql/spotterService/conversation-service.js.map +1 -0
  102. package/cjs/src/utils/graphql/spotterService/nls-answer-queries.d.ts +2 -0
  103. package/cjs/src/utils/graphql/spotterService/nls-answer-queries.d.ts.map +1 -0
  104. package/cjs/src/utils/graphql/spotterService/nls-answer-queries.js +403 -0
  105. package/cjs/src/utils/graphql/spotterService/nls-answer-queries.js.map +1 -0
  106. package/cjs/src/utils/graphql/spotterService/nls-answer-service.d.ts +12 -0
  107. package/cjs/src/utils/graphql/spotterService/nls-answer-service.d.ts.map +1 -0
  108. package/cjs/src/utils/graphql/spotterService/nls-answer-service.js +55 -0
  109. package/cjs/src/utils/graphql/spotterService/nls-answer-service.js.map +1 -0
  110. package/cjs/src/utils.d.ts +15 -0
  111. package/cjs/src/utils.d.ts.map +1 -1
  112. package/cjs/src/utils.js +33 -1
  113. package/cjs/src/utils.js.map +1 -1
  114. package/cjs/src/utils.spec.js +49 -0
  115. package/cjs/src/utils.spec.js.map +1 -1
  116. package/dist/{index-BdkKLLo1.js → index-0serzuii.js} +1 -1
  117. package/dist/index-CqrIh3Vj.js +7370 -0
  118. package/dist/index-aFN9fe8V.js +7371 -0
  119. package/dist/src/css-variables.d.ts +48 -0
  120. package/dist/src/css-variables.d.ts.map +1 -1
  121. package/dist/src/embed/app.d.ts +37 -0
  122. package/dist/src/embed/app.d.ts.map +1 -1
  123. package/dist/src/embed/auto-frame-renderer.d.ts +3 -0
  124. package/dist/src/embed/auto-frame-renderer.d.ts.map +1 -0
  125. package/dist/src/embed/base.d.ts.map +1 -1
  126. package/dist/src/embed/conversation.d.ts +128 -10
  127. package/dist/src/embed/conversation.d.ts.map +1 -1
  128. package/dist/src/embed/liveboard.d.ts +38 -1
  129. package/dist/src/embed/liveboard.d.ts.map +1 -1
  130. package/dist/src/errors.d.ts +1 -0
  131. package/dist/src/errors.d.ts.map +1 -1
  132. package/dist/src/index.d.ts +4 -3
  133. package/dist/src/index.d.ts.map +1 -1
  134. package/dist/src/types.d.ts +196 -41
  135. package/dist/src/types.d.ts.map +1 -1
  136. package/dist/src/utils/graphql/answerService/answer-queries.d.ts +1 -0
  137. package/dist/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -1
  138. package/dist/src/utils/graphql/answerService/answerService.d.ts +2 -1
  139. package/dist/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  140. package/dist/src/utils.d.ts +15 -0
  141. package/dist/src/utils.d.ts.map +1 -1
  142. package/dist/tsembed-react.es.js +365 -52
  143. package/dist/tsembed-react.js +364 -51
  144. package/dist/tsembed.es.js +429 -53
  145. package/dist/tsembed.js +428 -51
  146. package/dist/visual-embed-sdk-react-full.d.ts +697 -343
  147. package/dist/visual-embed-sdk-react.d.ts +686 -332
  148. package/dist/visual-embed-sdk.d.ts +761 -380
  149. package/lib/package.json +2 -2
  150. package/lib/src/css-variables.d.ts +48 -0
  151. package/lib/src/css-variables.d.ts.map +1 -1
  152. package/lib/src/embed/app.d.ts +37 -0
  153. package/lib/src/embed/app.d.ts.map +1 -1
  154. package/lib/src/embed/app.js +39 -3
  155. package/lib/src/embed/app.js.map +1 -1
  156. package/lib/src/embed/app.spec.js +24 -0
  157. package/lib/src/embed/app.spec.js.map +1 -1
  158. package/lib/src/embed/auto-frame-renderer.d.ts +3 -0
  159. package/lib/src/embed/auto-frame-renderer.d.ts.map +1 -0
  160. package/lib/src/embed/auto-frame-renderer.js +66 -0
  161. package/lib/src/embed/auto-frame-renderer.js.map +1 -0
  162. package/lib/src/embed/base.d.ts.map +1 -1
  163. package/lib/src/embed/base.js.map +1 -1
  164. package/lib/src/embed/conversation.d.ts +128 -10
  165. package/lib/src/embed/conversation.d.ts.map +1 -1
  166. package/lib/src/embed/conversation.js +42 -19
  167. package/lib/src/embed/conversation.js.map +1 -1
  168. package/lib/src/embed/conversation.spec.js +96 -3
  169. package/lib/src/embed/conversation.spec.js.map +1 -1
  170. package/lib/src/embed/liveboard.d.ts +38 -1
  171. package/lib/src/embed/liveboard.d.ts.map +1 -1
  172. package/lib/src/embed/liveboard.js +39 -11
  173. package/lib/src/embed/liveboard.js.map +1 -1
  174. package/lib/src/embed/liveboard.spec.js +179 -7
  175. package/lib/src/embed/liveboard.spec.js.map +1 -1
  176. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts +2 -0
  177. package/lib/src/embed/searchEmbed-basic-auth.spec.d.ts.map +1 -0
  178. package/lib/src/embed/searchEmbed-basic-auth.spec.js +101 -0
  179. package/lib/src/embed/searchEmbed-basic-auth.spec.js.map +1 -0
  180. package/lib/src/errors.d.ts +1 -0
  181. package/lib/src/errors.d.ts.map +1 -1
  182. package/lib/src/errors.js +1 -0
  183. package/lib/src/errors.js.map +1 -1
  184. package/lib/src/index.d.ts +4 -3
  185. package/lib/src/index.d.ts.map +1 -1
  186. package/lib/src/index.js +1 -0
  187. package/lib/src/index.js.map +1 -1
  188. package/lib/src/types.d.ts +196 -41
  189. package/lib/src/types.d.ts.map +1 -1
  190. package/lib/src/types.js +185 -19
  191. package/lib/src/types.js.map +1 -1
  192. package/lib/src/utils/graphql/answerService/answer-queries.d.ts +1 -0
  193. package/lib/src/utils/graphql/answerService/answer-queries.d.ts.map +1 -1
  194. package/lib/src/utils/graphql/answerService/answer-queries.js +22 -0
  195. package/lib/src/utils/graphql/answerService/answer-queries.js.map +1 -1
  196. package/lib/src/utils/graphql/answerService/answerService.d.ts +2 -1
  197. package/lib/src/utils/graphql/answerService/answerService.d.ts.map +1 -1
  198. package/lib/src/utils/graphql/answerService/answerService.js +9 -1
  199. package/lib/src/utils/graphql/answerService/answerService.js.map +1 -1
  200. package/lib/src/utils/graphql/answerService/answerService.spec.js +73 -0
  201. package/lib/src/utils/graphql/answerService/answerService.spec.js.map +1 -1
  202. package/lib/src/utils/graphql/conversationService/conversation-queries.d.ts +3 -0
  203. package/lib/src/utils/graphql/conversationService/conversation-queries.d.ts.map +1 -0
  204. package/lib/src/utils/graphql/conversationService/conversation-queries.js +315 -0
  205. package/lib/src/utils/graphql/conversationService/conversation-queries.js.map +1 -0
  206. package/lib/src/utils/graphql/conversationService/conversation-service.d.ts +12 -0
  207. package/lib/src/utils/graphql/conversationService/conversation-service.d.ts.map +1 -0
  208. package/lib/src/utils/graphql/conversationService/conversation-service.js +84 -0
  209. package/lib/src/utils/graphql/conversationService/conversation-service.js.map +1 -0
  210. package/lib/src/utils/graphql/spotterService/conversation-queries.d.ts +3 -0
  211. package/lib/src/utils/graphql/spotterService/conversation-queries.d.ts.map +1 -0
  212. package/lib/src/utils/graphql/spotterService/conversation-queries.js +315 -0
  213. package/lib/src/utils/graphql/spotterService/conversation-queries.js.map +1 -0
  214. package/lib/src/utils/graphql/spotterService/conversation-service.d.ts +12 -0
  215. package/lib/src/utils/graphql/spotterService/conversation-service.d.ts.map +1 -0
  216. package/lib/src/utils/graphql/spotterService/conversation-service.js +84 -0
  217. package/lib/src/utils/graphql/spotterService/conversation-service.js.map +1 -0
  218. package/lib/src/utils/graphql/spotterService/nls-answer-queries.d.ts +2 -0
  219. package/lib/src/utils/graphql/spotterService/nls-answer-queries.d.ts.map +1 -0
  220. package/lib/src/utils/graphql/spotterService/nls-answer-queries.js +400 -0
  221. package/lib/src/utils/graphql/spotterService/nls-answer-queries.js.map +1 -0
  222. package/lib/src/utils/graphql/spotterService/nls-answer-service.d.ts +12 -0
  223. package/lib/src/utils/graphql/spotterService/nls-answer-service.d.ts.map +1 -0
  224. package/lib/src/utils/graphql/spotterService/nls-answer-service.js +50 -0
  225. package/lib/src/utils/graphql/spotterService/nls-answer-service.js.map +1 -0
  226. package/lib/src/utils.d.ts +15 -0
  227. package/lib/src/utils.d.ts.map +1 -1
  228. package/lib/src/utils.js +30 -0
  229. package/lib/src/utils.js.map +1 -1
  230. package/lib/src/utils.spec.js +50 -1
  231. package/lib/src/utils.spec.js.map +1 -1
  232. package/lib/src/visual-embed-sdk.d.ts +7186 -0
  233. package/package.json +3 -3
  234. package/src/css-variables.ts +60 -0
  235. package/src/embed/app.spec.ts +32 -0
  236. package/src/embed/app.ts +97 -1
  237. package/src/embed/auto-frame-renderer.ts +78 -0
  238. package/src/embed/base.ts +1 -0
  239. package/src/embed/conversation.spec.ts +117 -3
  240. package/src/embed/conversation.ts +189 -30
  241. package/src/embed/liveboard.spec.ts +264 -10
  242. package/src/embed/liveboard.ts +100 -11
  243. package/src/errors.ts +1 -0
  244. package/src/index.ts +8 -1
  245. package/src/types.ts +198 -42
  246. package/src/utils/graphql/answerService/answer-queries.ts +23 -0
  247. package/src/utils/graphql/answerService/answerService.spec.ts +87 -0
  248. package/src/utils/graphql/answerService/answerService.ts +13 -1
  249. package/src/utils.spec.ts +56 -0
  250. package/src/utils.ts +36 -0
@@ -473,6 +473,93 @@ describe('Answer service tests', () => {
473
473
  expect(sql).toBe('SELECT * FROM table');
474
474
  });
475
475
 
476
+ test('Get SQL query with all columns should update display mode', async () => {
477
+ fetchMock.mockResponses(
478
+ JSON.stringify({
479
+ data: {
480
+ Answer__updateProperties: {
481
+ id: {
482
+ ...defaultSession,
483
+ },
484
+ answer: {
485
+ id: 'answer1',
486
+ displayMode: 'TABLE_MODE',
487
+ suggestedDisplayMode: 'CHART_MODE',
488
+ },
489
+ },
490
+ },
491
+ }),
492
+ JSON.stringify({
493
+ data: {
494
+ Answer__getQuery: {
495
+ sql: 'SELECT * FROM table',
496
+ },
497
+ },
498
+ }),
499
+ );
500
+ const answerService = createAnswerService();
501
+ const sql = await answerService.getSQLQuery(true);
502
+ expect(fetchMock).toHaveBeenCalledWith(
503
+ 'https://tshost/prism/?op=UpdateDisplayMode',
504
+ expect.objectContaining({
505
+ body: JSON.stringify({
506
+ operationName: 'UpdateDisplayMode',
507
+ query: queries.updateDisplayMode,
508
+ variables: {
509
+ session: defaultSession,
510
+ displayMode: 'TABLE_MODE',
511
+ },
512
+ }),
513
+ }),
514
+ );
515
+ expect(fetchMock).toHaveBeenCalledWith(
516
+ 'https://tshost/prism/?op=GetSQLQuery',
517
+ expect.objectContaining({
518
+ body: JSON.stringify({
519
+ operationName: 'GetSQLQuery',
520
+ query: getSQLQuery,
521
+ variables: {
522
+ session: defaultSession,
523
+ },
524
+ }),
525
+ }),
526
+ );
527
+ expect(sql).toBe('SELECT * FROM table');
528
+ });
529
+
530
+ test('Update display mode should call the right API', async () => {
531
+ fetchMock.mockResponseOnce(JSON.stringify({
532
+ data: {
533
+ Answer__updateProperties: {
534
+ id: {
535
+ ...defaultSession,
536
+ },
537
+ answer: {
538
+ id: 'answer1',
539
+ displayMode: 'CHART_MODE',
540
+ suggestedDisplayMode: 'TABLE_MODE',
541
+ },
542
+ },
543
+ },
544
+ }));
545
+ const answerService = createAnswerService();
546
+ const response = await answerService.updateDisplayMode('CHART_MODE');
547
+ expect(fetchMock).toHaveBeenCalledWith(
548
+ 'https://tshost/prism/?op=UpdateDisplayMode',
549
+ expect.objectContaining({
550
+ body: JSON.stringify({
551
+ operationName: 'UpdateDisplayMode',
552
+ query: queries.updateDisplayMode,
553
+ variables: {
554
+ session: defaultSession,
555
+ displayMode: 'CHART_MODE',
556
+ },
557
+ }),
558
+ }),
559
+ );
560
+ expect(response.answer.displayMode).toBe('CHART_MODE');
561
+ });
562
+
476
563
  test('Add displayed Viz should call the right API', async () => {
477
564
  fetchMock.mockResponseOnce(JSON.stringify({
478
565
  data: {
@@ -190,7 +190,19 @@ export class AnswerService {
190
190
  );
191
191
  }
192
192
 
193
- public async getSQLQuery(): Promise<string> {
193
+ public async updateDisplayMode(displayMode = "TABLE_MODE") {
194
+ return this.executeQuery(
195
+ queries.updateDisplayMode,
196
+ {
197
+ displayMode,
198
+ },
199
+ );
200
+ }
201
+
202
+ public async getSQLQuery(fetchSQLWithAllColumns = false): Promise<string> {
203
+ if (fetchSQLWithAllColumns) {
204
+ await this.updateDisplayMode("TABLE_MODE");
205
+ }
194
206
  const { sql } = await this.executeQuery(
195
207
  queries.getSQLQuery,
196
208
  {},
package/src/utils.spec.ts CHANGED
@@ -21,6 +21,8 @@ import {
21
21
  formatTemplate,
22
22
  isValidCssMargin,
23
23
  resetValueFromWindow,
24
+ validateHttpUrl,
25
+ setParamIfDefined,
24
26
  } from './utils';
25
27
  import { RuntimeFilterOp } from './types';
26
28
  import { logger } from './utils/logger';
@@ -824,4 +826,58 @@ describe('getValueFromWindow and storeValueInWindow', () => {
824
826
  expect(getValueFromWindow('key2')).toBe('value2');
825
827
  });
826
828
  });
829
+
830
+ describe('validateHttpUrl', () => {
831
+ test.each([
832
+ ['http URL', 'http://example.com'],
833
+ ['https URL', 'https://example.com'],
834
+ ['https URL with path', 'https://docs.example.com/spotter'],
835
+ ['https URL with query params', 'https://example.com/path?foo=bar'],
836
+ ])('should return [true, null] for valid %s', (_, url) => {
837
+ const [isValid, error] = validateHttpUrl(url);
838
+ expect(isValid).toBe(true);
839
+ expect(error).toBeNull();
840
+ });
841
+
842
+ test.each([
843
+ ['ftp protocol', 'ftp://example.com', 'ftp:'],
844
+ ['file protocol', 'file:///path/to/file', 'file:'],
845
+ ['javascript protocol', 'javascript:alert(1)', 'javascript:'],
846
+ ])('should return [false, Error] for %s', (_, url, protocol) => {
847
+ const [isValid, error] = validateHttpUrl(url);
848
+ expect(isValid).toBe(false);
849
+ expect(error).toBeInstanceOf(Error);
850
+ expect(error?.message).toContain('Invalid protocol');
851
+ expect(error?.message).toContain(protocol);
852
+ });
853
+
854
+ test.each([
855
+ ['invalid URL format', 'not-a-valid-url'],
856
+ ['empty string', ''],
857
+ ['URL without protocol', 'example.com'],
858
+ ])('should return [false, Error] for %s', (_, url) => {
859
+ const [isValid, error] = validateHttpUrl(url);
860
+ expect(isValid).toBe(false);
861
+ expect(error).toBeInstanceOf(Error);
862
+ });
863
+ });
864
+
865
+ describe('setParamIfDefined', () => {
866
+ test.each([
867
+ ['string value', 'testParam', 'testValue', false, 'testValue'],
868
+ ['number value', 'numParam', 42, false, 42],
869
+ ['truthy value as boolean', 'boolParam', 'truthy', true, true],
870
+ ['falsy value as boolean', 'boolParam', 0, true, false],
871
+ ])('should set %s correctly', (_, param, value, asBoolean, expected) => {
872
+ const queryParams: Record<string, unknown> = {};
873
+ setParamIfDefined(queryParams, param, value, asBoolean);
874
+ expect(queryParams[param]).toBe(expected);
875
+ });
876
+
877
+ test('should not set param when value is undefined', () => {
878
+ const queryParams: Record<string, unknown> = {};
879
+ setParamIfDefined(queryParams, 'testParam', undefined);
880
+ expect(queryParams.testParam).toBeUndefined();
881
+ });
882
+ });
827
883
  });
package/src/utils.ts CHANGED
@@ -581,3 +581,39 @@ export const isWindowUndefined = (): boolean => {
581
581
  }
582
582
  return false;
583
583
  }
584
+
585
+ /**
586
+ * Validates that a URL uses only http: or https: protocols.
587
+ * Returns a tuple of [isValid, error] so the caller can handle validation errors.
588
+ * @param url - The URL string to validate
589
+ * @returns [true, null] if valid, [false, Error] if invalid
590
+ */
591
+ export const validateHttpUrl = (url: string): [boolean, Error | null] => {
592
+ try {
593
+ const parsedUrl = new URL(url);
594
+ if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') {
595
+ return [false, new Error(`Invalid protocol: ${parsedUrl.protocol}. Only http: and https: are allowed.`)];
596
+ }
597
+ return [true, null];
598
+ } catch (error) {
599
+ return [false, error instanceof Error ? error : new Error(String(error))];
600
+ }
601
+ };
602
+
603
+ /**
604
+ * Sets a query parameter if the value is defined.
605
+ * @param queryParams - The query params object to modify
606
+ * @param param - The parameter key
607
+ * @param value - The value to set
608
+ * @param asBoolean - If true, coerces value to boolean
609
+ */
610
+ export const setParamIfDefined = <T>(
611
+ queryParams: Record<string, unknown>,
612
+ param: string,
613
+ value: T | undefined,
614
+ asBoolean = false,
615
+ ): void => {
616
+ if (value !== undefined) {
617
+ queryParams[param] = asBoolean ? !!value : value;
618
+ }
619
+ };