@tellescope/sdk 1.256.16 → 2.0.0-beta.1

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 (310) hide show
  1. package/CLAUDE.md +5 -0
  2. package/env.example +39 -0
  3. package/lib/cjs/enduser.d.ts +1 -0
  4. package/lib/cjs/enduser.d.ts.map +1 -1
  5. package/lib/cjs/sdk.d.ts +6 -1
  6. package/lib/cjs/sdk.d.ts.map +1 -1
  7. package/lib/cjs/sdk.js +2 -0
  8. package/lib/cjs/sdk.js.map +1 -1
  9. package/lib/cjs/session.d.ts +1 -0
  10. package/lib/cjs/session.d.ts.map +1 -1
  11. package/lib/cjs/tests/api_tests/active_patient_count.test.js +1 -1
  12. package/lib/cjs/tests/api_tests/active_patient_count.test.js.map +1 -1
  13. package/lib/cjs/tests/api_tests/appointment_rescheduled_trigger.test.js +1 -1
  14. package/lib/cjs/tests/api_tests/appointment_rescheduled_trigger.test.js.map +1 -1
  15. package/lib/cjs/tests/api_tests/calendar_event_limits.test.js +5 -5
  16. package/lib/cjs/tests/api_tests/calendar_event_limits.test.js.map +1 -1
  17. package/lib/cjs/tests/api_tests/calendar_event_references_update.test.js +1 -1
  18. package/lib/cjs/tests/api_tests/calendar_event_references_update.test.js.map +1 -1
  19. package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js +1 -1
  20. package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
  21. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts +21 -0
  22. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -0
  23. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js +215 -0
  24. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js.map +1 -0
  25. package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.js +6 -6
  26. package/lib/cjs/tests/api_tests/enduser_login_rate_limits.test.js.map +1 -1
  27. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js +3 -3
  28. package/lib/cjs/tests/api_tests/enduser_session_invalidation.test.js.map +1 -1
  29. package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts +32 -0
  30. package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -0
  31. package/lib/cjs/tests/api_tests/load_threads_care_team.test.js +257 -0
  32. package/lib/cjs/tests/api_tests/load_threads_care_team.test.js.map +1 -0
  33. package/lib/cjs/tests/api_tests/managed_content_enduser_access.test.js +1 -1
  34. package/lib/cjs/tests/api_tests/managed_content_enduser_access.test.js.map +1 -1
  35. package/lib/cjs/tests/api_tests/managed_content_file_access.test.js +1 -1
  36. package/lib/cjs/tests/api_tests/managed_content_file_access.test.js.map +1 -1
  37. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
  38. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js +139 -9
  39. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
  40. package/lib/cjs/tests/api_tests/mdi_webhooks.test.d.ts.map +1 -1
  41. package/lib/cjs/tests/api_tests/mdi_webhooks.test.js +21 -8
  42. package/lib/cjs/tests/api_tests/mdi_webhooks.test.js.map +1 -1
  43. package/lib/cjs/tests/api_tests/nutrition_tracking.test.js +1 -1
  44. package/lib/cjs/tests/api_tests/nutrition_tracking.test.js.map +1 -1
  45. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js +1 -1
  46. package/lib/cjs/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  47. package/lib/cjs/tests/api_tests/programs.test.d.ts +5 -0
  48. package/lib/cjs/tests/api_tests/programs.test.d.ts.map +1 -0
  49. package/lib/cjs/tests/api_tests/programs.test.js +370 -0
  50. package/lib/cjs/tests/api_tests/programs.test.js.map +1 -0
  51. package/lib/cjs/tests/api_tests/programs_trigger_filters.test.d.ts +5 -0
  52. package/lib/cjs/tests/api_tests/programs_trigger_filters.test.d.ts.map +1 -0
  53. package/lib/cjs/tests/api_tests/programs_trigger_filters.test.js +295 -0
  54. package/lib/cjs/tests/api_tests/programs_trigger_filters.test.js.map +1 -0
  55. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
  56. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +55 -47
  57. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
  58. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js +1 -1
  59. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -1
  60. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +42 -0
  61. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -0
  62. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js +484 -0
  63. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -0
  64. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js +1 -1
  65. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -1
  66. package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js +1 -1
  67. package/lib/cjs/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js.map +1 -1
  68. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js +1 -1
  69. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js.map +1 -1
  70. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js +1 -1
  71. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -1
  72. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js +2 -2
  73. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -1
  74. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +32 -0
  75. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -0
  76. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +297 -0
  77. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -0
  78. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -1
  79. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +4 -1
  80. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -1
  81. package/lib/cjs/tests/api_tests/templated_message_date_fields.test.d.ts +3 -0
  82. package/lib/cjs/tests/api_tests/templated_message_date_fields.test.d.ts.map +1 -0
  83. package/lib/cjs/tests/api_tests/templated_message_date_fields.test.js +202 -0
  84. package/lib/cjs/tests/api_tests/templated_message_date_fields.test.js.map +1 -0
  85. package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.d.ts +3 -0
  86. package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.d.ts.map +1 -0
  87. package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.js +233 -0
  88. package/lib/cjs/tests/api_tests/templated_message_link_in_href.test.js.map +1 -0
  89. package/lib/cjs/tests/api_tests/templated_message_links.test.d.ts +3 -0
  90. package/lib/cjs/tests/api_tests/templated_message_links.test.d.ts.map +1 -0
  91. package/lib/cjs/tests/api_tests/templated_message_links.test.js +235 -0
  92. package/lib/cjs/tests/api_tests/templated_message_links.test.js.map +1 -0
  93. package/lib/cjs/tests/api_tests/time_tracks.test.js +1 -1
  94. package/lib/cjs/tests/api_tests/time_tracks.test.js.map +1 -1
  95. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts +5 -0
  96. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -0
  97. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js +395 -0
  98. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js.map +1 -0
  99. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts +5 -0
  100. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -0
  101. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js +471 -0
  102. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -0
  103. package/lib/cjs/tests/api_tests/user_portal_settings.test.js +1 -1
  104. package/lib/cjs/tests/api_tests/user_portal_settings.test.js.map +1 -1
  105. package/lib/cjs/tests/api_tests/validation_behavior_locks.test.d.ts +2 -0
  106. package/lib/cjs/tests/api_tests/validation_behavior_locks.test.d.ts.map +1 -0
  107. package/lib/cjs/tests/api_tests/validation_behavior_locks.test.js +233 -0
  108. package/lib/cjs/tests/api_tests/validation_behavior_locks.test.js.map +1 -0
  109. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts +5 -0
  110. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -0
  111. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js +569 -0
  112. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js.map +1 -0
  113. package/lib/cjs/tests/public_endpoint_tests.js +3 -2
  114. package/lib/cjs/tests/public_endpoint_tests.js.map +1 -1
  115. package/lib/cjs/tests/setup.d.ts +16 -0
  116. package/lib/cjs/tests/setup.d.ts.map +1 -1
  117. package/lib/cjs/tests/setup.js +55 -8
  118. package/lib/cjs/tests/setup.js.map +1 -1
  119. package/lib/cjs/tests/setup_isolated.d.ts +9 -0
  120. package/lib/cjs/tests/setup_isolated.d.ts.map +1 -1
  121. package/lib/cjs/tests/setup_isolated.js +23 -2
  122. package/lib/cjs/tests/setup_isolated.js.map +1 -1
  123. package/lib/cjs/tests/socket_tests.d.ts.map +1 -1
  124. package/lib/cjs/tests/socket_tests.js +3 -2
  125. package/lib/cjs/tests/socket_tests.js.map +1 -1
  126. package/lib/cjs/tests/tests.d.ts.map +1 -1
  127. package/lib/cjs/tests/tests.js +244 -216
  128. package/lib/cjs/tests/tests.js.map +1 -1
  129. package/lib/cjs/tests/unit_tests/trigger_title_partials.test.d.ts +3 -0
  130. package/lib/cjs/tests/unit_tests/trigger_title_partials.test.d.ts.map +1 -0
  131. package/lib/cjs/tests/unit_tests/trigger_title_partials.test.js +118 -0
  132. package/lib/cjs/tests/unit_tests/trigger_title_partials.test.js.map +1 -0
  133. package/lib/cjs/tests/webhooks_tests.js +3 -2
  134. package/lib/cjs/tests/webhooks_tests.js.map +1 -1
  135. package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  136. package/lib/esm/enduser.d.ts +2 -0
  137. package/lib/esm/enduser.d.ts.map +1 -1
  138. package/lib/esm/sdk.d.ts +9 -3
  139. package/lib/esm/sdk.d.ts.map +1 -1
  140. package/lib/esm/sdk.js +2 -0
  141. package/lib/esm/sdk.js.map +1 -1
  142. package/lib/esm/session.d.ts +2 -0
  143. package/lib/esm/session.d.ts.map +1 -1
  144. package/lib/esm/tests/api_tests/active_patient_count.test.js +1 -1
  145. package/lib/esm/tests/api_tests/active_patient_count.test.js.map +1 -1
  146. package/lib/esm/tests/api_tests/appointment_rescheduled_trigger.test.js +1 -1
  147. package/lib/esm/tests/api_tests/appointment_rescheduled_trigger.test.js.map +1 -1
  148. package/lib/esm/tests/api_tests/calendar_event_limits.test.js +5 -5
  149. package/lib/esm/tests/api_tests/calendar_event_limits.test.js.map +1 -1
  150. package/lib/esm/tests/api_tests/calendar_event_references_update.test.js +1 -1
  151. package/lib/esm/tests/api_tests/calendar_event_references_update.test.js.map +1 -1
  152. package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js +1 -1
  153. package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
  154. package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts +21 -0
  155. package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -0
  156. package/lib/esm/tests/api_tests/enduser_fields_sync.test.js +210 -0
  157. package/lib/esm/tests/api_tests/enduser_fields_sync.test.js.map +1 -0
  158. package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.js +7 -7
  159. package/lib/esm/tests/api_tests/enduser_login_rate_limits.test.js.map +1 -1
  160. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js +4 -4
  161. package/lib/esm/tests/api_tests/enduser_session_invalidation.test.js.map +1 -1
  162. package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts +32 -0
  163. package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -0
  164. package/lib/esm/tests/api_tests/load_threads_care_team.test.js +252 -0
  165. package/lib/esm/tests/api_tests/load_threads_care_team.test.js.map +1 -0
  166. package/lib/esm/tests/api_tests/managed_content_enduser_access.test.js +1 -1
  167. package/lib/esm/tests/api_tests/managed_content_enduser_access.test.js.map +1 -1
  168. package/lib/esm/tests/api_tests/managed_content_file_access.test.js +1 -1
  169. package/lib/esm/tests/api_tests/managed_content_file_access.test.js.map +1 -1
  170. package/lib/esm/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
  171. package/lib/esm/tests/api_tests/mdi_case_offerings.test.js +139 -9
  172. package/lib/esm/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
  173. package/lib/esm/tests/api_tests/mdi_webhooks.test.d.ts.map +1 -1
  174. package/lib/esm/tests/api_tests/mdi_webhooks.test.js +21 -8
  175. package/lib/esm/tests/api_tests/mdi_webhooks.test.js.map +1 -1
  176. package/lib/esm/tests/api_tests/nutrition_tracking.test.js +1 -1
  177. package/lib/esm/tests/api_tests/nutrition_tracking.test.js.map +1 -1
  178. package/lib/esm/tests/api_tests/openloop_webhooks.test.js +1 -1
  179. package/lib/esm/tests/api_tests/openloop_webhooks.test.js.map +1 -1
  180. package/lib/esm/tests/api_tests/programs.test.d.ts +5 -0
  181. package/lib/esm/tests/api_tests/programs.test.d.ts.map +1 -0
  182. package/lib/esm/tests/api_tests/programs.test.js +365 -0
  183. package/lib/esm/tests/api_tests/programs.test.js.map +1 -0
  184. package/lib/esm/tests/api_tests/programs_trigger_filters.test.d.ts +5 -0
  185. package/lib/esm/tests/api_tests/programs_trigger_filters.test.d.ts.map +1 -0
  186. package/lib/esm/tests/api_tests/programs_trigger_filters.test.js +290 -0
  187. package/lib/esm/tests/api_tests/programs_trigger_filters.test.js.map +1 -0
  188. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
  189. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +55 -47
  190. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
  191. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js +1 -1
  192. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -1
  193. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +42 -0
  194. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -0
  195. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js +479 -0
  196. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -0
  197. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js +1 -1
  198. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -1
  199. package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js +1 -1
  200. package/lib/esm/tests/api_tests/security/enduser-ai-summary-trust-gate.test.js.map +1 -1
  201. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js +1 -1
  202. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js.map +1 -1
  203. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js +1 -1
  204. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -1
  205. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js +3 -3
  206. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -1
  207. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +32 -0
  208. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -0
  209. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +292 -0
  210. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -0
  211. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -1
  212. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +4 -1
  213. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -1
  214. package/lib/esm/tests/api_tests/templated_message_date_fields.test.d.ts +3 -0
  215. package/lib/esm/tests/api_tests/templated_message_date_fields.test.d.ts.map +1 -0
  216. package/lib/esm/tests/api_tests/templated_message_date_fields.test.js +198 -0
  217. package/lib/esm/tests/api_tests/templated_message_date_fields.test.js.map +1 -0
  218. package/lib/esm/tests/api_tests/templated_message_link_in_href.test.d.ts +3 -0
  219. package/lib/esm/tests/api_tests/templated_message_link_in_href.test.d.ts.map +1 -0
  220. package/lib/esm/tests/api_tests/templated_message_link_in_href.test.js +229 -0
  221. package/lib/esm/tests/api_tests/templated_message_link_in_href.test.js.map +1 -0
  222. package/lib/esm/tests/api_tests/templated_message_links.test.d.ts +3 -0
  223. package/lib/esm/tests/api_tests/templated_message_links.test.d.ts.map +1 -0
  224. package/lib/esm/tests/api_tests/templated_message_links.test.js +231 -0
  225. package/lib/esm/tests/api_tests/templated_message_links.test.js.map +1 -0
  226. package/lib/esm/tests/api_tests/time_tracks.test.js +1 -1
  227. package/lib/esm/tests/api_tests/time_tracks.test.js.map +1 -1
  228. package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts +5 -0
  229. package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -0
  230. package/lib/esm/tests/api_tests/trigger_contains_matching.test.js +390 -0
  231. package/lib/esm/tests/api_tests/trigger_contains_matching.test.js.map +1 -0
  232. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts +5 -0
  233. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -0
  234. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js +466 -0
  235. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -0
  236. package/lib/esm/tests/api_tests/user_portal_settings.test.js +1 -1
  237. package/lib/esm/tests/api_tests/user_portal_settings.test.js.map +1 -1
  238. package/lib/esm/tests/api_tests/validation_behavior_locks.test.d.ts +2 -0
  239. package/lib/esm/tests/api_tests/validation_behavior_locks.test.d.ts.map +1 -0
  240. package/lib/esm/tests/api_tests/validation_behavior_locks.test.js +229 -0
  241. package/lib/esm/tests/api_tests/validation_behavior_locks.test.js.map +1 -0
  242. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts +5 -0
  243. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -0
  244. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js +564 -0
  245. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js.map +1 -0
  246. package/lib/esm/tests/public_endpoint_tests.js +3 -2
  247. package/lib/esm/tests/public_endpoint_tests.js.map +1 -1
  248. package/lib/esm/tests/setup.d.ts +16 -0
  249. package/lib/esm/tests/setup.d.ts.map +1 -1
  250. package/lib/esm/tests/setup.js +53 -7
  251. package/lib/esm/tests/setup.js.map +1 -1
  252. package/lib/esm/tests/setup_isolated.d.ts +9 -0
  253. package/lib/esm/tests/setup_isolated.d.ts.map +1 -1
  254. package/lib/esm/tests/setup_isolated.js +21 -1
  255. package/lib/esm/tests/setup_isolated.js.map +1 -1
  256. package/lib/esm/tests/socket_tests.d.ts.map +1 -1
  257. package/lib/esm/tests/socket_tests.js +3 -2
  258. package/lib/esm/tests/socket_tests.js.map +1 -1
  259. package/lib/esm/tests/tests.d.ts.map +1 -1
  260. package/lib/esm/tests/tests.js +244 -216
  261. package/lib/esm/tests/tests.js.map +1 -1
  262. package/lib/esm/tests/unit_tests/trigger_title_partials.test.d.ts +3 -0
  263. package/lib/esm/tests/unit_tests/trigger_title_partials.test.d.ts.map +1 -0
  264. package/lib/esm/tests/unit_tests/trigger_title_partials.test.js +115 -0
  265. package/lib/esm/tests/unit_tests/trigger_title_partials.test.js.map +1 -0
  266. package/lib/esm/tests/webhooks_tests.js +3 -2
  267. package/lib/esm/tests/webhooks_tests.js.map +1 -1
  268. package/lib/tsconfig.tsbuildinfo +1 -1
  269. package/package.json +22 -19
  270. package/src/sdk.ts +11 -1
  271. package/src/tests/api_tests/active_patient_count.test.ts +1 -1
  272. package/src/tests/api_tests/appointment_rescheduled_trigger.test.ts +1 -1
  273. package/src/tests/api_tests/calendar_event_references_update.test.ts +1 -1
  274. package/src/tests/api_tests/enduser_cross_access_isolation.test.ts +1 -1
  275. package/src/tests/api_tests/enduser_fields_sync.test.ts +150 -0
  276. package/src/tests/api_tests/enduser_login_rate_limits.test.ts +7 -7
  277. package/src/tests/api_tests/enduser_session_invalidation.test.ts +4 -4
  278. package/src/tests/api_tests/load_threads_care_team.test.ts +210 -0
  279. package/src/tests/api_tests/managed_content_enduser_access.test.ts +1 -1
  280. package/src/tests/api_tests/managed_content_file_access.test.ts +1 -1
  281. package/src/tests/api_tests/mdi_case_offerings.test.ts +138 -0
  282. package/src/tests/api_tests/mdi_webhooks.test.ts +9 -0
  283. package/src/tests/api_tests/nutrition_tracking.test.ts +1 -1
  284. package/src/tests/api_tests/openloop_webhooks.test.ts +1 -1
  285. package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +33 -26
  286. package/src/tests/api_tests/security/allowed-paths-confinement.test.ts +1 -1
  287. package/src/tests/api_tests/security/appointment-reschedule-ownership.test.ts +432 -0
  288. package/src/tests/api_tests/security/booking-info-idor-token-mint.test.ts +1 -1
  289. package/src/tests/api_tests/security/enduser-ai-summary-trust-gate.test.ts +1 -1
  290. package/src/tests/api_tests/security/enduser-cross-create.test.ts +1 -1
  291. package/src/tests/api_tests/security/enduser-write-restrictions.test.ts +1 -1
  292. package/src/tests/api_tests/security/invite-user-enumeration.test.ts +3 -3
  293. package/src/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.ts +197 -0
  294. package/src/tests/api_tests/security/sanitize-user-html-with-iframes.test.ts +4 -1
  295. package/src/tests/api_tests/templated_message_date_fields.test.ts +146 -0
  296. package/src/tests/api_tests/templated_message_links.test.ts +192 -0
  297. package/src/tests/api_tests/time_tracks.test.ts +1 -1
  298. package/src/tests/api_tests/trigger_contains_matching.test.ts +345 -0
  299. package/src/tests/api_tests/user_logs_restore_deleted.test.ts +415 -0
  300. package/src/tests/api_tests/user_portal_settings.test.ts +1 -1
  301. package/src/tests/api_tests/validation_behavior_locks.test.ts +204 -0
  302. package/src/tests/public_endpoint_tests.ts +3 -2
  303. package/src/tests/setup.ts +43 -7
  304. package/src/tests/setup_isolated.ts +10 -0
  305. package/src/tests/socket_tests.ts +3 -2
  306. package/src/tests/tests.ts +22 -2
  307. package/src/tests/unit_tests/trigger_title_partials.test.ts +174 -0
  308. package/src/tests/webhooks_tests.ts +4 -2
  309. package/test_generated.pdf +0 -0
  310. package/tsconfig.cjs.json +17 -0
package/package.json CHANGED
@@ -1,25 +1,25 @@
1
1
  {
2
2
  "name": "@tellescope/sdk",
3
- "version": "1.256.16",
3
+ "version": "2.0.0-beta.1",
4
4
  "description": "Code for interacting with the Tellescope API",
5
5
  "main": "./lib/cjs/sdk.js",
6
6
  "module": "./lib/esm/sdk.js",
7
7
  "types": "./lib/esm/sdk.d.ts",
8
8
  "scripts": {
9
- "test": "npm run-script build:cjs && npm run-script retest",
9
+ "test": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest",
10
10
  "retest": "npm run-script retest-api && npm run-script retest-public-api && npm run-script retest-webhooks",
11
- "test-api": "npm run-script build:cjs && npm run-script retest-api",
11
+ "test-api": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-api",
12
12
  "retest-api": "node -r dotenv/config lib/cjs/tests/tests.js",
13
- "test-public-api": "npm run-script build:cjs && npm run-script retest-public-api",
13
+ "test-public-api": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-public-api",
14
14
  "retest-public-api": "node -r dotenv/config lib/cjs/tests/public_endpoint_tests.js",
15
- "test-sockets": "npm run-script build:cjs && npm run-script retest-sockets",
15
+ "test-sockets": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-sockets",
16
16
  "retest-sockets": "node -r dotenv/config lib/cjs/tests/socket_tests.js",
17
- "test-webhooks": "npm run-script build:cjs && npm run-script retest-webhooks",
17
+ "test-webhooks": "npm run-script build:cjs && node ../../../scripts/dev/check-build-fresh.mjs && npm run-script retest-webhooks",
18
18
  "retest-webhooks": "node -r dotenv/config lib/cjs/tests/webhooks_tests.js",
19
19
  "build": "npm run-script build:esm && npm run-script build:cjs",
20
20
  "build:esm": "tsc",
21
- "build:cjs": "tsc --module commonjs --outDir lib/cjs",
22
- "watch:cjs": "tsc --module commonjs --outDir lib/cjs --watch"
21
+ "build:cjs": "tsc -b tsconfig.cjs.json",
22
+ "watch:cjs": "tsc -b -w --preserveWatchOutput tsconfig.cjs.json"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",
@@ -31,18 +31,21 @@
31
31
  "url": "https://github.com/tellescope-os/tellescope/issues"
32
32
  },
33
33
  "homepage": "https://github.com/tellescope-os/tellescope#readme",
34
+ "engines": {
35
+ "node": ">=22.12.0"
36
+ },
34
37
  "dependencies": {
35
- "@tellescope/constants": "1.256.16",
36
- "@tellescope/schema": "1.256.16",
37
- "@tellescope/testing": "1.256.16",
38
- "@tellescope/types-client": "1.256.16",
39
- "@tellescope/types-models": "1.256.16",
40
- "@tellescope/types-utilities": "1.256.16",
41
- "@tellescope/utilities": "1.256.16",
42
- "@tellescope/validation": "1.256.16",
43
- "axios": "0.30.3",
38
+ "@tellescope/constants": "2.0.0-beta.1",
39
+ "@tellescope/schema": "2.0.0-beta.1",
40
+ "@tellescope/testing": "2.0.0-beta.1",
41
+ "@tellescope/types-client": "2.0.0-beta.1",
42
+ "@tellescope/types-models": "2.0.0-beta.1",
43
+ "@tellescope/types-utilities": "2.0.0-beta.1",
44
+ "@tellescope/utilities": "2.0.0-beta.1",
45
+ "@tellescope/validation": "2.0.0-beta.1",
46
+ "axios": "0.33.0",
44
47
  "dotenv": "14.3.2",
45
- "form-data": "4.0.4",
48
+ "form-data": "4.0.6",
46
49
  "source-map-support": "0.5.21"
47
50
  },
48
51
  "devDependencies": {
@@ -55,5 +58,5 @@
55
58
  "publishConfig": {
56
59
  "access": "public"
57
60
  },
58
- "gitHead": "5874f0e4ca75d8ce70768a5933cd5bb5b3b15c58"
61
+ "gitHead": "20732ce4e382a7d81dde795ce9c7194c2aca1ce6"
59
62
  }
package/src/sdk.ts CHANGED
@@ -234,6 +234,11 @@ const loadDefaultQueries = (s: Session): { [K in keyof ClientModelForName] : API
234
234
  })
235
235
 
236
236
  type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
237
+ user_logs: {
238
+ restore_deleted: (args: extractFields<CustomActions['user_logs']['restore_deleted']['parameters']>) => (
239
+ Promise<extractFields<CustomActions['user_logs']['restore_deleted']['returns']>>
240
+ ),
241
+ },
237
242
  inbox_threads: {
238
243
  build_threads: (args: extractFields<CustomActions['inbox_threads']['build_threads']['parameters']>) => (
239
244
  Promise<extractFields<CustomActions['inbox_threads']['build_threads']['returns']>>
@@ -313,6 +318,9 @@ type Queries = { [K in keyof ClientModelForName]: APIQuery<K> } & {
313
318
  get_form_statistics: (args: extractFields<CustomActions['forms']['get_form_statistics']['parameters']>) => (
314
319
  Promise<extractFields<CustomActions['forms']['get_form_statistics']['returns']>>
315
320
  ),
321
+ public_form_details: (args: extractFields<PublicActions['forms']['public_form_details']['parameters']>) => (
322
+ Promise<extractFields<PublicActions['forms']['public_form_details']['returns']>>
323
+ ),
316
324
  },
317
325
  phone_trees: {
318
326
  start_outbound_call: (args: extractFields<CustomActions['phone_trees']['start_outbound_call']['parameters']>) => (
@@ -993,6 +1001,7 @@ export class Session extends SessionManager {
993
1001
  queries.api_keys.get_organization_api_keys = () => this._GET(`/v1${schema.api_keys.customActions.get_organization_api_keys.path}`)
994
1002
  queries.api_keys.delete_organization_api_key = ({ id }) => this._DELETE(`/v1/organization-api-key/${id}`)
995
1003
 
1004
+ queries.user_logs.restore_deleted = a => this._POST(`/v1${schema.user_logs.customActions.restore_deleted.path}`, a)
996
1005
  queries.enduser_orders.get_available_tests = a => this._GET(`/v1${schema.enduser_orders.customActions.get_available_tests.path}`, a)
997
1006
  queries.enduser_orders.create_lab_order = a => this._POST(`/v1${schema.enduser_orders.customActions.create_lab_order.path}`, a)
998
1007
  queries.enduser_orders.cancel_order = a => this._POST(`/v1${schema.enduser_orders.customActions.cancel_order.path}`, a)
@@ -1052,6 +1061,7 @@ export class Session extends SessionManager {
1052
1061
  queries.organizations.invite_user = a => this._POST(`/v1/${schema.users.customActions.invite_user.path}`, a)
1053
1062
 
1054
1063
  queries.forms.get_form_statistics = args => this._GET(`/v1${schema.forms.customActions.get_form_statistics.path}`, args)
1064
+ queries.forms.public_form_details = args => this._GET(`/v1${schema.forms.publicActions.public_form_details.path}`, args)
1055
1065
 
1056
1066
  queries.form_fields.load_choices_from_database = args => this._GET(`/v1${schema.form_fields.customActions.load_choices_from_database.path}`, args)
1057
1067
  queries.form_fields.booking_info = args => this._GET(`/v1${schema.form_fields.customActions.booking_info.path}`, args)
@@ -1350,4 +1360,4 @@ export class Session extends SessionManager {
1350
1360
  sync = (a: { from: Date }) => this.GET<typeof a, { results: Pick<DataSyncRecord, 'modelName' | 'recordId' | 'data'>[], to: string }>('/v1/data-sync', a)
1351
1361
  }
1352
1362
 
1353
- export { SessionOptions }
1363
+ export type { SessionOptions }
@@ -10,7 +10,7 @@ import {
10
10
  import { authenticate_enduser_via_token, setup_tests } from "../setup"
11
11
 
12
12
  const host = process.env.API_URL || 'http://localhost:8080' as const
13
- const businessId = '60398b1131a295e64f084ff6'
13
+ const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
14
14
 
15
15
  // Second-tenant session, used to prove the active patient count never crosses tenants.
16
16
  // Same key used by the org-isolation tests in tests.ts.
@@ -3,7 +3,7 @@ import { log_header, wait, async_test } from "@tellescope/testing"
3
3
  import { Enduser } from "@tellescope/types-client"
4
4
  import { setup_tests } from "../setup"
5
5
 
6
- const host = process.env.TEST_HOST || "http://localhost:8080"
6
+ const host = process.env.API_URL || process.env.TEST_HOST || "http://localhost:8080"
7
7
 
8
8
  export const appointment_rescheduled_trigger_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
9
9
  log_header("Automation Trigger Tests (Appointment Rescheduled)")
@@ -8,7 +8,7 @@ import {
8
8
  import { setup_tests, authenticate_enduser_via_token } from "../setup"
9
9
 
10
10
  const host = process.env.API_URL || 'http://localhost:8080' as const
11
- const businessId = '60398b1131a295e64f084ff6'
11
+ const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
12
12
 
13
13
  /**
14
14
  * CU-86e2dvrn9: calendar_events.references switched from updatesDisabled to
@@ -12,7 +12,7 @@ import { ModelName } from "@tellescope/types-models"
12
12
  import { authenticate_enduser_via_token, setup_tests } from "../setup"
13
13
 
14
14
  const host = process.env.API_URL || 'http://localhost:8080' as const
15
- const businessId = '60398b1131a295e64f084ff6'
15
+ const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
16
16
 
17
17
  type OwnerField = 'enduserId' | 'enduserIds' | 'attendees.id'
18
18
 
@@ -0,0 +1,150 @@
1
+ require('source-map-support').install();
2
+
3
+ import {
4
+ assert_checks,
5
+ Check,
6
+ setup_isolated_tenant,
7
+ } from "../setup_isolated"
8
+
9
+ const host = process.env.API_URL || 'http://localhost:8080' as const
10
+
11
+ const RAND = () => Math.random().toString(36).slice(2, 10)
12
+
13
+ /**
14
+ * Schema round-trip for `Form.syncEnduserFieldsToEHR` and `Form.enduserFieldsSync`, the two fields
15
+ * behind "Sync Patient Fields to Healthie".
16
+ *
17
+ * The names are deliberately EHR-agnostic — each row's `externalId` is the question id in whichever
18
+ * EHR the form syncs to (`Form.externalId`) — even though only the Healthie push reads them today.
19
+ *
20
+ * What this file can and cannot cover: the actual `createFormAnswerGroup` round trip needs live
21
+ * Healthie credentials that no suite here has, and there is no Healthie mock or fixture layer in the
22
+ * repository, so the push itself is covered by the resolver's unit tests
23
+ * (api/modules/forms.test.ts) plus manual QA against Healthie staging. What IS covered here is
24
+ * everything the API owns: that the fields persist and read back unchanged, that validation rejects
25
+ * malformed rows, and — the one that guards a real decision in the form-editor modal — that an
26
+ * update with `replaceObjectFields` actually SHRINKS the list rather than merging into it, which is
27
+ * what makes deleting a mapping row persist.
28
+ */
29
+ export const enduser_fields_sync_checks = async (): Promise<Check[]> => {
30
+ const checks: Check[] = []
31
+ const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
32
+
33
+ /** records a check that the given write is rejected (any 4xx), not silently accepted */
34
+ const record_rejected = async (label: string, write: () => Promise<any>) => {
35
+ try {
36
+ const accepted = await write()
37
+ record(label, false, `accepted, expected a validation error — got ${JSON.stringify(accepted?.enduserFieldsSync)}`)
38
+ } catch (err: any) {
39
+ const message = err?.message || String(err)
40
+ record(label, true, `rejected: ${message.slice(0, 200)}`)
41
+ }
42
+ }
43
+
44
+ let tenant: Awaited<ReturnType<typeof setup_isolated_tenant>> | undefined
45
+
46
+ try {
47
+ tenant = await setup_isolated_tenant({ name: `FieldSync ${RAND()}`, users: [{ isAdmin: true }], apiKeys: 0 })
48
+ const sdk = tenant.sdk
49
+
50
+ const TWO_ROWS = [
51
+ { field: 'dateOfBirth', externalId: '111111' },
52
+ { field: 'Referring Provider', externalId: '222222' },
53
+ ]
54
+
55
+ /* ===== 1. Create with both fields set, and read them back ===== */
56
+ const created = await sdk.api.forms.createOne({
57
+ title: `Field sync ${RAND()}`,
58
+ externalId: 'healthie-form-id',
59
+ syncEnduserFieldsToEHR: true,
60
+ enduserFieldsSync: TWO_ROWS,
61
+ })
62
+ const readBack = await sdk.api.forms.getOne(created.id)
63
+
64
+ record('1: create persists syncEnduserFieldsToEHR', readBack.syncEnduserFieldsToEHR === true,
65
+ `got ${JSON.stringify(readBack.syncEnduserFieldsToEHR)}`)
66
+ record('1: create persists enduserFieldsSync verbatim',
67
+ JSON.stringify(readBack.enduserFieldsSync) === JSON.stringify(TWO_ROWS),
68
+ `got ${JSON.stringify(readBack.enduserFieldsSync)} expected ${JSON.stringify(TWO_ROWS)}`)
69
+
70
+ /* ===== 2. replaceObjectFields must SHRINK the list, not merge into it =====
71
+ This is the guard for the form-editor modal's `replaceObjectFields` change. Without it,
72
+ deleting a mapping row in the UI would save and then silently come back on reload. */
73
+ const ONE_ROW = [{ field: 'insurance.memberId', externalId: '333333' }]
74
+ await sdk.api.forms.updateOne(created.id, { enduserFieldsSync: ONE_ROW }, { replaceObjectFields: true })
75
+ const shrunk = await sdk.api.forms.getOne(created.id)
76
+
77
+ record('2: replaceObjectFields shrinks a two-row list to one row',
78
+ JSON.stringify(shrunk.enduserFieldsSync) === JSON.stringify(ONE_ROW),
79
+ `got ${JSON.stringify(shrunk.enduserFieldsSync)} expected ${JSON.stringify(ONE_ROW)}`)
80
+
81
+ /* ===== 3. Malformed rows are rejected ===== */
82
+ await record_rejected('3: a row with a non-string field is rejected', () =>
83
+ sdk.api.forms.updateOne(created.id, { enduserFieldsSync: [{ field: 1, externalId: '1' }] } as any,
84
+ { replaceObjectFields: true }))
85
+
86
+ await record_rejected('3: a row missing externalId is rejected', () =>
87
+ sdk.api.forms.updateOne(created.id, { enduserFieldsSync: [{ field: 'fname' }] } as any,
88
+ { replaceObjectFields: true }))
89
+
90
+ await record_rejected('3: an externalId over 100 characters is rejected', () =>
91
+ sdk.api.forms.updateOne(created.id,
92
+ { enduserFieldsSync: [{ field: 'fname', externalId: 'x'.repeat(101) }] },
93
+ { replaceObjectFields: true }))
94
+
95
+ // a rejected write must not have partially applied - the one-row list from step 2 stands
96
+ const afterRejections = await sdk.api.forms.getOne(created.id)
97
+ record('3: rejected writes leave the stored list untouched',
98
+ JSON.stringify(afterRejections.enduserFieldsSync) === JSON.stringify(ONE_ROW),
99
+ `got ${JSON.stringify(afterRejections.enduserFieldsSync)} expected ${JSON.stringify(ONE_ROW)}`)
100
+
101
+ /* ===== 4. An empty list is accepted (clearing every row) ===== */
102
+ try {
103
+ await sdk.api.forms.updateOne(created.id, { enduserFieldsSync: [] }, { replaceObjectFields: true })
104
+ const cleared = await sdk.api.forms.getOne(created.id)
105
+ const rows = cleared.enduserFieldsSync
106
+ record('4: an empty list is accepted and clears the mapping',
107
+ Array.isArray(rows) && rows.length === 0,
108
+ `got ${JSON.stringify(rows)}`)
109
+ } catch (err: any) {
110
+ record('4: an empty list is accepted and clears the mapping', false,
111
+ `rejected: ${(err?.message || String(err)).slice(0, 200)}`)
112
+ }
113
+
114
+ /* ===== 5. Backward compatibility: a form that sets neither field reads back with neither ===== */
115
+ const plain = await sdk.api.forms.createOne({ title: `No field sync ${RAND()}` })
116
+ const plainReadBack = await sdk.api.forms.getOne(plain.id)
117
+
118
+ record('5: a form created without either field has no syncEnduserFieldsToEHR',
119
+ plainReadBack.syncEnduserFieldsToEHR === undefined,
120
+ `got ${JSON.stringify(plainReadBack.syncEnduserFieldsToEHR)}`)
121
+ record('5: a form created without either field has no enduserFieldsSync',
122
+ plainReadBack.enduserFieldsSync === undefined,
123
+ `got ${JSON.stringify(plainReadBack.enduserFieldsSync)}`)
124
+ } finally {
125
+ // Teardown BEFORE anything asserts — assert calls process.exit, which skips pending finallys and
126
+ // would strand the provisioned tenant permanently.
127
+ if (tenant) { try { await tenant.teardown() } catch (e) { console.error('teardown failed', e) } }
128
+ }
129
+
130
+ return checks
131
+ }
132
+
133
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
134
+ export const enduser_fields_sync_tests = async () => {
135
+ assert_checks('Enduser fields sync schema round-trip', await enduser_fields_sync_checks())
136
+ }
137
+
138
+ // Allow running this test file independently
139
+ if (require.main === module) {
140
+ console.log(`🌐 Using API URL: ${host}`)
141
+ enduser_fields_sync_tests()
142
+ .then(() => {
143
+ console.log("✅ enduser fields sync test suite completed successfully")
144
+ process.exit(0)
145
+ })
146
+ .catch((error) => {
147
+ console.error("❌ enduser fields sync test suite failed:", error)
148
+ process.exit(1)
149
+ })
150
+ }
@@ -6,7 +6,7 @@ import {
6
6
  async_test,
7
7
  log_header,
8
8
  } from "@tellescope/testing"
9
- import { setup_tests } from "../setup"
9
+ import { reset_test_tenant, setup_tests } from "../setup"
10
10
 
11
11
  const host = process.env.API_URL || 'http://localhost:8080' as const
12
12
 
@@ -43,7 +43,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
43
43
  const businessId = sdk.userInfo.businessId
44
44
 
45
45
  // Ensure throttled_events from prior tests don't bleed in.
46
- await sdk.reset_db()
46
+ await reset_test_tenant(sdk)
47
47
 
48
48
  // ---- /v1/login-enduser per-IP cap (20/min) ----
49
49
  // Bogus emails ensure we never reach the actual DB lookup / login work.
@@ -58,7 +58,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
58
58
  { expectedResult: 'tripped' }
59
59
  )
60
60
 
61
- await sdk.reset_db()
61
+ await reset_test_tenant(sdk)
62
62
 
63
63
  // ---- /v1/begin-enduser-login-flow per-IP cap (10/min) ----
64
64
  // Use distinct emails so the per-identifier limit (5/10min) does NOT trip first;
@@ -73,7 +73,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
73
73
  { expectedResult: 'tripped' }
74
74
  )
75
75
 
76
- await sdk.reset_db()
76
+ await reset_test_tenant(sdk)
77
77
 
78
78
  // ---- /v1/begin-enduser-login-flow per-identifier cap (5 / 10 min per email) ----
79
79
  // Hit a single email below the per-IP cap.
@@ -88,7 +88,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
88
88
  { expectedResult: 'tripped' }
89
89
  )
90
90
 
91
- await sdk.reset_db()
91
+ await reset_test_tenant(sdk)
92
92
 
93
93
  // ---- /v1/endusers/send-otp-code per-IP cap (5/min) ----
94
94
  // Use a bogus JWT-shaped token so we trip the IP guard first (it runs before any DB work).
@@ -116,7 +116,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
116
116
 
117
117
  // ---- Legitimate-login regression: a single successful login should still go through ----
118
118
  // Reset state, then create a real enduser with a password and confirm one login succeeds.
119
- await sdk.reset_db()
119
+ await reset_test_tenant(sdk)
120
120
 
121
121
  const ts = Date.now()
122
122
  const enduser = await sdk.api.endusers.createOne({
@@ -151,7 +151,7 @@ export const enduser_login_rate_limits_tests = async ({ sdk, sdkNonAdmin } : { s
151
151
  )
152
152
  } finally {
153
153
  await sdk.api.endusers.deleteOne(enduser.id).catch(() => null)
154
- await sdk.reset_db().catch(() => null)
154
+ await reset_test_tenant(sdk).catch(() => null)
155
155
  }
156
156
  }
157
157
 
@@ -7,10 +7,10 @@ import {
7
7
  log_header,
8
8
  wait,
9
9
  } from "@tellescope/testing"
10
- import { setup_tests } from "../setup"
10
+ import { reset_test_tenant, setup_tests } from "../setup"
11
11
 
12
12
  const host = process.env.API_URL || 'http://localhost:8080' as const
13
- const businessId = '60398b1131a295e64f084ff6'
13
+ const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
14
14
 
15
15
  // Main test function that can be called independently
16
16
  export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
@@ -126,7 +126,7 @@ export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } :
126
126
  }
127
127
 
128
128
  // Ensure OTP is off and db is clean before starting
129
- await sdk.reset_db()
129
+ await reset_test_tenant(sdk)
130
130
  await wait(undefined, 500)
131
131
  await resetOTPSettings(sdk)
132
132
 
@@ -242,7 +242,7 @@ export const enduser_session_invalidation_tests = async ({ sdk, sdkNonAdmin } :
242
242
  )
243
243
 
244
244
  // Reset rate limiting state before continuing
245
- await sdk.reset_db()
245
+ await reset_test_tenant(sdk)
246
246
  await wait(undefined, 500)
247
247
 
248
248
  // Test 8: Disabling OTP does NOT invalidate sessions
@@ -0,0 +1,210 @@
1
+ require('source-map-support').install();
2
+
3
+ import {
4
+ assert_checks,
5
+ Check,
6
+ setup_isolated_tenant,
7
+ } from "../setup_isolated"
8
+
9
+ const host = process.env.API_URL || 'http://localhost:8080' as const
10
+
11
+ const RAND = () => Math.random().toString(36).slice(2, 10)
12
+
13
+ /**
14
+ * Coverage for the `careTeamUserIds` parameter on `load_threads` (GET /v1/inbox-threads/load).
15
+ *
16
+ * The caller used to resolve "every patient these users are on the care team for" client-side and
17
+ * push the resulting enduser ids into `mdbFilter`. For a care team of hundreds of patients that
18
+ * overflows the GET query string (Node's 16KB header limit) before any Tellescope code runs. The
19
+ * parameter moves the join to the server: the API resolves the user ids to endusers via
20
+ * `assignedTo`, and ANDs `{ enduserIds: { $in: [...] } }` onto the thread filter.
21
+ *
22
+ * `setup_isolated` rather than `setup_tests` because several checks assert on the COMPLETE thread
23
+ * set of the tenant. On the shared fixture org, `build_threads` and the other inbox suites leave
24
+ * threads behind and every "returns exactly these" assertion would be flaky.
25
+ *
26
+ * The threads are created directly with `inbox_threads.createOne` (the model exposes create for
27
+ * exactly this reason) rather than via `build_threads`, so the tenant contains exactly three.
28
+ *
29
+ * Check 4 is the one that catches a wrong implementation: a care team with no patients resolves to
30
+ * an empty enduser list, and the clause must still be applied (returning nothing) rather than
31
+ * dropped (returning everything). Check 5 is its counterweight — it rules out an implementation
32
+ * that returns nothing for every call, which would satisfy 3, 4 and 7c vacuously.
33
+ *
34
+ * Check 7 is the permission boundary. A provisioned non-admin gets
35
+ * `sms_messages: ASSIGNED_AND_DEFAULT_ACCESS` (read: 'Assigned'), and the fixture threads carry
36
+ * `userIds: []`, so that session sees a thread only through the assigned-enduser branch. 7c fails
37
+ * if the care-team clause is ever pushed into the access `$or` instead of `filter.$and`, i.e. if
38
+ * the parameter could be used to WIDEN what a session can see.
39
+ */
40
+ export const load_threads_care_team_checks = async (): Promise<Check[]> => {
41
+ const checks: Check[] = []
42
+ const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
43
+
44
+ let tenant: Awaited<ReturnType<typeof setup_isolated_tenant>> | undefined
45
+
46
+ try {
47
+ /* ================= fixtures ================= */
48
+
49
+ tenant = await setup_isolated_tenant({
50
+ name: `LoadThreadsCareTeam ${RAND()}`,
51
+ users: [{ isAdmin: true }, { roles: ['Non-Admin'] }],
52
+ apiKeys: 0,
53
+ })
54
+
55
+ const { sdk, sdkNonAdmin } = tenant
56
+ const NA = tenant.users[1].id // the non-admin's own user id
57
+
58
+ // care-team users: they never log in, they exist only to appear in endusers.assignedTo
59
+ const [U1, U2, U3] = await Promise.all([1, 2, 3].map(n => sdk.api.users.createOne({
60
+ email: `dontsend-care-team-${n}-${RAND()}@tellescope.com`,
61
+ fname: 'CareTeam', lname: `User${n}`,
62
+ verifiedEmail: true,
63
+ notificationEmailsDisabled: true,
64
+ })))
65
+
66
+ // E1 is also assigned to the non-admin, so check 7a has a non-empty baseline to measure against
67
+ const E1 = await sdk.api.endusers.createOne({ fname: 'CareTeam', lname: 'PatientOne', assignedTo: [U1.id, NA] })
68
+ const E2 = await sdk.api.endusers.createOne({ fname: 'CareTeam', lname: 'PatientTwo', assignedTo: [U2.id] })
69
+ const E3 = await sdk.api.endusers.createOne({ fname: 'CareTeam', lname: 'PatientThree', assignedTo: [] })
70
+
71
+ const now = Date.now()
72
+ const MINUTE = 60 * 1000
73
+ const defaultThreadFields = {
74
+ type: 'SMS' as const,
75
+ assignedTo: [],
76
+ userIds: [],
77
+ inboxStatus: 'New',
78
+ preview: 'care team test',
79
+ title: 'Care team test',
80
+ }
81
+
82
+ const T1 = await sdk.api.inbox_threads.createOne({
83
+ ...defaultThreadFields,
84
+ enduserIds: [E1.id], threadId: `care-team-t1-${RAND()}`, timestamp: new Date(now - 3 * MINUTE),
85
+ })
86
+ const T2 = await sdk.api.inbox_threads.createOne({
87
+ ...defaultThreadFields,
88
+ enduserIds: [E2.id], threadId: `care-team-t2-${RAND()}`, timestamp: new Date(now - 2 * MINUTE),
89
+ })
90
+ const T3 = await sdk.api.inbox_threads.createOne({
91
+ ...defaultThreadFields,
92
+ enduserIds: [E3.id], threadId: `care-team-t3-${RAND()}`, timestamp: new Date(now - 1 * MINUTE),
93
+ })
94
+
95
+ const label_for: { [id: string]: string } = { [T1.id]: 'T1', [T2.id]: 'T2', [T3.id]: 'T3' }
96
+ const describe = (ids: string[]) => `[${ids.map(id => label_for[id] || id).sort().join(', ')}]`
97
+ const load = async (args: any, session = sdk) => (
98
+ (await session.api.inbox_threads.load_threads(args)).threads.map(t => t.id)
99
+ )
100
+ const same_set = (a: string[], b: string[]) => (
101
+ a.length === b.length && [...a].sort().join() === [...b].sort().join()
102
+ )
103
+
104
+ /* ===== 1: a single care-team user ===== */
105
+
106
+ const one = await load({ careTeamUserIds: [U1.id] })
107
+ record(
108
+ '1: careTeamUserIds=[U1] returns exactly the thread for U1\'s patient',
109
+ same_set(one, [T1.id]),
110
+ `got ${describe(one)} (want [T1])`,
111
+ )
112
+
113
+ /* ===== 2: multiple care-team users union their patients ===== */
114
+
115
+ const two = await load({ careTeamUserIds: [U1.id, U2.id] })
116
+ record(
117
+ '2: careTeamUserIds=[U1, U2] returns both their threads and not the unassigned one',
118
+ same_set(two, [T1.id, T2.id]),
119
+ `got ${describe(two)} (want [T1, T2] — T3\'s enduser has an empty care team)`,
120
+ )
121
+
122
+ /* ===== 3: ANDs with enduserIds rather than ORing ===== */
123
+
124
+ const intersect = await load({ careTeamUserIds: [U2.id], enduserIds: [E1.id] })
125
+ record(
126
+ '3: careTeamUserIds AND enduserIds narrows to the intersection',
127
+ intersect.length === 0,
128
+ `got ${describe(intersect)} (want [] — E1 is not on U2\'s care team; a non-empty result means the`
129
+ + ` two filters OR instead of AND)`,
130
+ )
131
+
132
+ /* ===== 4: an empty resolution must not degrade to "no filter" ===== */
133
+
134
+ const emptyCareTeam = await load({ careTeamUserIds: [U3.id] })
135
+ record(
136
+ '4: a care-team user with no patients returns no threads, not every thread',
137
+ emptyCareTeam.length === 0,
138
+ `got ${describe(emptyCareTeam)} (want [] — returning all three means the clause was dropped when`
139
+ + ` the enduser lookup came back empty)`,
140
+ )
141
+
142
+ /* ===== 5: the feature-off path is unchanged ===== */
143
+
144
+ const baseline = await load({})
145
+ const emptyParam = await load({ careTeamUserIds: [] })
146
+ record(
147
+ '5: omitting careTeamUserIds, and passing [], both return exactly the three fixture threads',
148
+ same_set(baseline, [T1.id, T2.id, T3.id]) && same_set(emptyParam, baseline),
149
+ `omitted=${describe(baseline)} empty=${describe(emptyParam)} (want [T1, T2, T3] for both)`,
150
+ )
151
+
152
+ /* ===== 6: the count path shares the filter ===== */
153
+
154
+ const { count } = await sdk.api.inbox_threads.load_threads({ careTeamUserIds: [U1.id], returnCount: true })
155
+ record(
156
+ '6: returnCount applies the care-team filter too',
157
+ count === 1,
158
+ `count=${count} (want 1 — 3 means the count path skipped the filter)`,
159
+ )
160
+
161
+ /* ===== 7: permission boundary — the parameter can only narrow, never widen ===== */
162
+
163
+ const naBaseline = await load({}, sdkNonAdmin)
164
+ record(
165
+ '7a: the non-admin\'s unfiltered inbox is exactly the thread for the patient assigned to them',
166
+ same_set(naBaseline, [T1.id]),
167
+ `got ${describe(naBaseline)} (want [T1] — the baseline the next two checks are measured against;`
168
+ + ` if this is empty, 7b and 7c pass vacuously)`,
169
+ )
170
+
171
+ const naOwnCareTeam = await load({ careTeamUserIds: [U1.id] }, sdkNonAdmin)
172
+ record(
173
+ '7b: the parameter does not remove a thread the non-admin legitimately sees',
174
+ same_set(naOwnCareTeam, [T1.id]),
175
+ `got ${describe(naOwnCareTeam)} (want [T1])`,
176
+ )
177
+
178
+ const naForeignCareTeam = await load({ careTeamUserIds: [U2.id] }, sdkNonAdmin)
179
+ record(
180
+ '7c: the parameter cannot reach a thread outside the non-admin\'s access',
181
+ naForeignCareTeam.length === 0,
182
+ `got ${describe(naForeignCareTeam)} (want [] — T2 matches U2\'s care team but is outside this`
183
+ + ` session's access; a non-empty result means the clause was pushed into the access $or)`,
184
+ )
185
+ } finally {
186
+ // Teardown BEFORE anything asserts — assert calls process.exit, which would strand the tenant.
187
+ if (tenant) { try { await tenant.teardown() } catch (e) { console.error('teardown failed', e) } }
188
+ }
189
+
190
+ return checks
191
+ }
192
+
193
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
194
+ export const load_threads_care_team_tests = async () => {
195
+ assert_checks('Load threads care team', await load_threads_care_team_checks())
196
+ }
197
+
198
+ // Allow running this test file independently
199
+ if (require.main === module) {
200
+ console.log(`🌐 Using API URL: ${host}`)
201
+ load_threads_care_team_tests()
202
+ .then(() => {
203
+ console.log("✅ Load threads care team test suite completed successfully")
204
+ process.exit(0)
205
+ })
206
+ .catch((error) => {
207
+ console.error("❌ Load threads care team test suite failed:", error)
208
+ process.exit(1)
209
+ })
210
+ }
@@ -9,7 +9,7 @@ import {
9
9
  import { setup_tests } from "../setup"
10
10
 
11
11
  const host = process.env.API_URL || 'http://localhost:8080' as const
12
- const businessId = '60398b1131a295e64f084ff6'
12
+ const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
13
13
 
14
14
  /**
15
15
  * Tests for managed_content_records enduser access filtering.
@@ -9,7 +9,7 @@ import {
9
9
  import { setup_tests } from "../setup"
10
10
 
11
11
  const host = process.env.API_URL || 'http://localhost:8080' as const
12
- const businessId = '60398b1131a295e64f084ff6'
12
+ const businessId = process.env.TEST_BUSINESS_ID || '60398b1131a295e64f084ff6'
13
13
 
14
14
  /**
15
15
  * Tests for file_download_URL enduser access via managed content fallback.