@tellescope/sdk 2.0.0-beta.1 → 2.0.0-beta.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 (293) hide show
  1. package/CLAUDE.md +24 -0
  2. package/env.example +6 -0
  3. package/lib/cjs/sdk.d.ts +373 -3
  4. package/lib/cjs/sdk.d.ts.map +1 -1
  5. package/lib/cjs/sdk.js.map +1 -1
  6. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts +22 -0
  7. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts.map +1 -0
  8. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.js +506 -0
  9. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.js.map +1 -0
  10. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts +3 -1
  11. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -1
  12. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js +3 -1
  13. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js.map +1 -1
  14. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts +3 -1
  15. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -1
  16. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js +3 -1
  17. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -1
  18. package/lib/cjs/tests/api_tests/form_vital_reading.test.d.ts +7 -0
  19. package/lib/cjs/tests/api_tests/form_vital_reading.test.d.ts.map +1 -0
  20. package/lib/cjs/tests/api_tests/form_vital_reading.test.js +461 -0
  21. package/lib/cjs/tests/api_tests/form_vital_reading.test.js.map +1 -0
  22. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.d.ts +26 -0
  23. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.d.ts.map +1 -0
  24. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.js +232 -0
  25. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.js.map +1 -0
  26. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.d.ts +34 -0
  27. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.d.ts.map +1 -0
  28. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.js +243 -0
  29. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.js.map +1 -0
  30. package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts +3 -1
  31. package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -1
  32. package/lib/cjs/tests/api_tests/load_threads_care_team.test.js +3 -1
  33. package/lib/cjs/tests/api_tests/load_threads_care_team.test.js.map +1 -1
  34. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +3 -1
  35. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -1
  36. package/lib/cjs/tests/api_tests/organization_signup.test.js +3 -1
  37. package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -1
  38. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.d.ts +7 -0
  39. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.d.ts.map +1 -0
  40. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.js +571 -0
  41. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.js.map +1 -0
  42. package/lib/cjs/tests/api_tests/programs.test.d.ts +3 -1
  43. package/lib/cjs/tests/api_tests/programs.test.d.ts.map +1 -1
  44. package/lib/cjs/tests/api_tests/programs.test.js +73 -17
  45. package/lib/cjs/tests/api_tests/programs.test.js.map +1 -1
  46. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.d.ts +7 -0
  47. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.d.ts.map +1 -0
  48. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.js +733 -0
  49. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.js.map +1 -0
  50. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +3 -1
  51. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
  52. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +3 -1
  53. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
  54. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +3 -1
  55. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -1
  56. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js +3 -1
  57. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -1
  58. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.d.ts +3 -1
  59. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.d.ts.map +1 -1
  60. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.js +3 -1
  61. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.js.map +1 -1
  62. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts +3 -1
  63. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -1
  64. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js +3 -1
  65. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -1
  66. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -1
  67. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +424 -39
  68. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -1
  69. package/lib/cjs/tests/api_tests/security/register_business_id_injection.test.js +2 -2
  70. package/lib/cjs/tests/api_tests/security/register_business_id_injection.test.js.map +1 -1
  71. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +3 -1
  72. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -1
  73. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +3 -1
  74. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -1
  75. package/lib/cjs/tests/api_tests/session_for_join_link.test.d.ts +10 -0
  76. package/lib/cjs/tests/api_tests/session_for_join_link.test.d.ts.map +1 -0
  77. package/lib/cjs/tests/api_tests/session_for_join_link.test.js +147 -0
  78. package/lib/cjs/tests/api_tests/session_for_join_link.test.js.map +1 -0
  79. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -1
  80. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js +191 -88
  81. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js.map +1 -1
  82. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.d.ts +17 -0
  83. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.d.ts.map +1 -0
  84. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.js +219 -0
  85. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.js.map +1 -0
  86. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.d.ts +5 -0
  87. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.d.ts.map +1 -0
  88. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.js +396 -0
  89. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.js.map +1 -0
  90. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +3 -1
  91. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -1
  92. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +3 -1
  93. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -1
  94. package/lib/cjs/tests/api_tests/totp_mfa.test.d.ts.map +1 -1
  95. package/lib/cjs/tests/api_tests/totp_mfa.test.js +37 -31
  96. package/lib/cjs/tests/api_tests/totp_mfa.test.js.map +1 -1
  97. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts +3 -1
  98. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -1
  99. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js +3 -1
  100. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js.map +1 -1
  101. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts +3 -1
  102. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -1
  103. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js +3 -1
  104. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -1
  105. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts +3 -1
  106. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -1
  107. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js +3 -1
  108. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js.map +1 -1
  109. package/lib/cjs/tests/api_tests/vitl_push.test.d.ts +7 -0
  110. package/lib/cjs/tests/api_tests/vitl_push.test.d.ts.map +1 -0
  111. package/lib/cjs/tests/api_tests/vitl_push.test.js +341 -0
  112. package/lib/cjs/tests/api_tests/vitl_push.test.js.map +1 -0
  113. package/lib/cjs/tests/setup_isolated.d.ts +48 -0
  114. package/lib/cjs/tests/setup_isolated.d.ts.map +1 -1
  115. package/lib/cjs/tests/setup_isolated.js +119 -1
  116. package/lib/cjs/tests/setup_isolated.js.map +1 -1
  117. package/lib/cjs/tests/tests.d.ts.map +1 -1
  118. package/lib/cjs/tests/tests.js +374 -292
  119. package/lib/cjs/tests/tests.js.map +1 -1
  120. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.d.ts +2 -0
  121. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.d.ts.map +1 -0
  122. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.js +201 -0
  123. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.js.map +1 -0
  124. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.d.ts +3 -0
  125. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.d.ts.map +1 -0
  126. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.js +120 -0
  127. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.js.map +1 -0
  128. package/lib/cjs/tests/webhooks_tests.js +44 -34
  129. package/lib/cjs/tests/webhooks_tests.js.map +1 -1
  130. package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  131. package/lib/esm/sdk.d.ts +373 -3
  132. package/lib/esm/sdk.d.ts.map +1 -1
  133. package/lib/esm/sdk.js.map +1 -1
  134. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts +22 -0
  135. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts.map +1 -0
  136. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.js +501 -0
  137. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.js.map +1 -0
  138. package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts +3 -1
  139. package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -1
  140. package/lib/esm/tests/api_tests/enduser_fields_sync.test.js +2 -0
  141. package/lib/esm/tests/api_tests/enduser_fields_sync.test.js.map +1 -1
  142. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts +3 -1
  143. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -1
  144. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js +2 -0
  145. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -1
  146. package/lib/esm/tests/api_tests/form_vital_reading.test.d.ts +7 -0
  147. package/lib/esm/tests/api_tests/form_vital_reading.test.d.ts.map +1 -0
  148. package/lib/esm/tests/api_tests/form_vital_reading.test.js +456 -0
  149. package/lib/esm/tests/api_tests/form_vital_reading.test.js.map +1 -0
  150. package/lib/esm/tests/api_tests/journey_statistics_cache.test.d.ts +26 -0
  151. package/lib/esm/tests/api_tests/journey_statistics_cache.test.d.ts.map +1 -0
  152. package/lib/esm/tests/api_tests/journey_statistics_cache.test.js +227 -0
  153. package/lib/esm/tests/api_tests/journey_statistics_cache.test.js.map +1 -0
  154. package/lib/esm/tests/api_tests/journey_step_manual_count.test.d.ts +34 -0
  155. package/lib/esm/tests/api_tests/journey_step_manual_count.test.d.ts.map +1 -0
  156. package/lib/esm/tests/api_tests/journey_step_manual_count.test.js +238 -0
  157. package/lib/esm/tests/api_tests/journey_step_manual_count.test.js.map +1 -0
  158. package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts +3 -1
  159. package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -1
  160. package/lib/esm/tests/api_tests/load_threads_care_team.test.js +2 -0
  161. package/lib/esm/tests/api_tests/load_threads_care_team.test.js.map +1 -1
  162. package/lib/esm/tests/api_tests/organization_signup.test.d.ts +3 -1
  163. package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -1
  164. package/lib/esm/tests/api_tests/organization_signup.test.js +2 -0
  165. package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -1
  166. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.d.ts +7 -0
  167. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.d.ts.map +1 -0
  168. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.js +566 -0
  169. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.js.map +1 -0
  170. package/lib/esm/tests/api_tests/programs.test.d.ts +3 -1
  171. package/lib/esm/tests/api_tests/programs.test.d.ts.map +1 -1
  172. package/lib/esm/tests/api_tests/programs.test.js +72 -16
  173. package/lib/esm/tests/api_tests/programs.test.js.map +1 -1
  174. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.d.ts +7 -0
  175. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.d.ts.map +1 -0
  176. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.js +728 -0
  177. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.js.map +1 -0
  178. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +3 -1
  179. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
  180. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +2 -0
  181. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
  182. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +3 -1
  183. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -1
  184. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js +2 -0
  185. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -1
  186. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.d.ts +3 -1
  187. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.d.ts.map +1 -1
  188. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.js +2 -0
  189. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.js.map +1 -1
  190. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts +3 -1
  191. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -1
  192. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js +2 -0
  193. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -1
  194. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -1
  195. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +424 -39
  196. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -1
  197. package/lib/esm/tests/api_tests/security/register_business_id_injection.test.js +2 -2
  198. package/lib/esm/tests/api_tests/security/register_business_id_injection.test.js.map +1 -1
  199. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +3 -1
  200. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -1
  201. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +2 -0
  202. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -1
  203. package/lib/esm/tests/api_tests/session_for_join_link.test.d.ts +10 -0
  204. package/lib/esm/tests/api_tests/session_for_join_link.test.d.ts.map +1 -0
  205. package/lib/esm/tests/api_tests/session_for_join_link.test.js +142 -0
  206. package/lib/esm/tests/api_tests/session_for_join_link.test.js.map +1 -0
  207. package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -1
  208. package/lib/esm/tests/api_tests/set_fields_order_templates.test.js +191 -88
  209. package/lib/esm/tests/api_tests/set_fields_order_templates.test.js.map +1 -1
  210. package/lib/esm/tests/api_tests/stripe_checkout_items.test.d.ts +17 -0
  211. package/lib/esm/tests/api_tests/stripe_checkout_items.test.d.ts.map +1 -0
  212. package/lib/esm/tests/api_tests/stripe_checkout_items.test.js +214 -0
  213. package/lib/esm/tests/api_tests/stripe_checkout_items.test.js.map +1 -0
  214. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.d.ts +5 -0
  215. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.d.ts.map +1 -0
  216. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.js +388 -0
  217. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.js.map +1 -0
  218. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +3 -1
  219. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -1
  220. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +2 -0
  221. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -1
  222. package/lib/esm/tests/api_tests/totp_mfa.test.d.ts.map +1 -1
  223. package/lib/esm/tests/api_tests/totp_mfa.test.js +37 -31
  224. package/lib/esm/tests/api_tests/totp_mfa.test.js.map +1 -1
  225. package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts +3 -1
  226. package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -1
  227. package/lib/esm/tests/api_tests/trigger_contains_matching.test.js +2 -0
  228. package/lib/esm/tests/api_tests/trigger_contains_matching.test.js.map +1 -1
  229. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts +3 -1
  230. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -1
  231. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js +2 -0
  232. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -1
  233. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts +3 -1
  234. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -1
  235. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js +2 -0
  236. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js.map +1 -1
  237. package/lib/esm/tests/api_tests/vitl_push.test.d.ts +7 -0
  238. package/lib/esm/tests/api_tests/vitl_push.test.d.ts.map +1 -0
  239. package/lib/esm/tests/api_tests/vitl_push.test.js +333 -0
  240. package/lib/esm/tests/api_tests/vitl_push.test.js.map +1 -0
  241. package/lib/esm/tests/setup_isolated.d.ts +48 -0
  242. package/lib/esm/tests/setup_isolated.d.ts.map +1 -1
  243. package/lib/esm/tests/setup_isolated.js +114 -0
  244. package/lib/esm/tests/setup_isolated.js.map +1 -1
  245. package/lib/esm/tests/tests.d.ts.map +1 -1
  246. package/lib/esm/tests/tests.js +375 -293
  247. package/lib/esm/tests/tests.js.map +1 -1
  248. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.d.ts +2 -0
  249. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.d.ts.map +1 -0
  250. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.js +174 -0
  251. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.js.map +1 -0
  252. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.d.ts +3 -0
  253. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.d.ts.map +1 -0
  254. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.js +117 -0
  255. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.js.map +1 -0
  256. package/lib/esm/tests/webhooks_tests.js +44 -34
  257. package/lib/esm/tests/webhooks_tests.js.map +1 -1
  258. package/lib/tsconfig.tsbuildinfo +1 -1
  259. package/package.json +12 -11
  260. package/src/sdk.ts +3 -2
  261. package/src/tests/api_tests/booking_prevent_upcoming_duplicate.test.ts +541 -0
  262. package/src/tests/api_tests/enduser_fields_sync.test.ts +4 -0
  263. package/src/tests/api_tests/enduser_observations_auto_review.test.ts +4 -0
  264. package/src/tests/api_tests/form_vital_reading.test.ts +393 -0
  265. package/src/tests/api_tests/journey_statistics_cache.test.ts +237 -0
  266. package/src/tests/api_tests/journey_step_manual_count.test.ts +220 -0
  267. package/src/tests/api_tests/load_threads_care_team.test.ts +4 -0
  268. package/src/tests/api_tests/organization_signup.test.ts +4 -0
  269. package/src/tests/api_tests/phone_embed_leave_message.test.ts +541 -0
  270. package/src/tests/api_tests/programs.test.ts +351 -0
  271. package/src/tests/api_tests/public_form_case_insensitive_match.test.ts +632 -0
  272. package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +4 -0
  273. package/src/tests/api_tests/security/appointment-reschedule-ownership.test.ts +4 -0
  274. package/src/tests/api_tests/security/file-link-session-confinement.test.ts +4 -0
  275. package/src/tests/api_tests/security/form-logic-js-execution.test.ts +4 -0
  276. package/src/tests/api_tests/security/formsort_webhook_auth_field_lock.test.ts +225 -0
  277. package/src/tests/api_tests/security/register_business_id_injection.test.ts +2 -2
  278. package/src/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.ts +4 -0
  279. package/src/tests/api_tests/session_for_join_link.test.ts +99 -0
  280. package/src/tests/api_tests/set_fields_order_templates.test.ts +94 -9
  281. package/src/tests/api_tests/stripe_checkout_items.test.ts +186 -0
  282. package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +4 -0
  283. package/src/tests/api_tests/totp_mfa.test.ts +10 -5
  284. package/src/tests/api_tests/trigger_contains_matching.test.ts +4 -0
  285. package/src/tests/api_tests/user_logs_restore_deleted.test.ts +4 -0
  286. package/src/tests/api_tests/vital_review_trigger_action.test.ts +455 -0
  287. package/src/tests/api_tests/vitl_push.test.ts +323 -0
  288. package/src/tests/setup_isolated.ts +140 -0
  289. package/src/tests/tests.ts +124 -30
  290. package/src/tests/unit_tests/isolated_suite_discovery.test.ts +178 -0
  291. package/src/tests/unit_tests/programs_conditional_logic.test.ts +189 -0
  292. package/src/tests/webhooks_tests.ts +6 -1
  293. package/test_generated.pdf +0 -0
@@ -0,0 +1,219 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ISOLATED_SUITE = exports.stripe_checkout_items_tests = exports.stripe_checkout_items_checks = void 0;
40
+ require('source-map-support').install();
41
+ var setup_isolated_1 = require("../setup_isolated");
42
+ var setup_1 = require("../setup");
43
+ var sdk_1 = require("../../sdk");
44
+ var host = process.env.API_URL || 'http://localhost:8080';
45
+ var RAND = function () { return Math.random().toString(36).slice(2, 10); };
46
+ var ENDUSER_RAW_IDS_MESSAGE = "stripePriceId and subscriptionPriceId are not supported for enduser sessions";
47
+ var QUANTITY_MESSAGE = "quantity must be a whole number of at least 1";
48
+ var EXACTLY_ONE_ID_MESSAGE = "Each items entry must specify exactly one of productId, stripePriceId, or subscriptionPriceId";
49
+ // what the handler throws once a request is past every items guard and no Stripe account is
50
+ // configured - the marker for "this cart was accepted, it only ran out of Stripe"
51
+ var NO_STRIPE_MESSAGE = "Stripe Connection not found";
52
+ /**
53
+ * The guards on the `items` parameter of products/prepare-stripe-checkout that run BEFORE any Stripe
54
+ * integration is resolved, so they are reachable without a Stripe account: the enduser rejection of
55
+ * raw Stripe price ids, the productIds/items exclusivity, the per-entry shape rules and the quantity
56
+ * validator.
57
+ *
58
+ * The quantity guards themselves (the cap of 99, the two "greater than 1" rejections) sit on the
59
+ * Stripe Custom branch and are pinned instead by the unit suite in
60
+ * packages/private/api/api/integrations/payments/stripe_line_items.test.ts, which needs no database
61
+ * and no Stripe account. Anything that creates or inspects a real Checkout session is manual QA.
62
+ */
63
+ var stripe_checkout_items_checks = function () { return __awaiter(void 0, void 0, void 0, function () {
64
+ var checks, record, tenant, sdk_2, businessId, product, enduser, enduserSDK_1, errorFor, enduserRawPrice, enduserRawSubscription, enduserProduct, userRawPrice, both, neither, noId, twoIds, unknownId, unknown, _i, _a, quantity, message, productIdsOnly, e_1;
65
+ return __generator(this, function (_b) {
66
+ switch (_b.label) {
67
+ case 0:
68
+ checks = [];
69
+ record = function (label, pass, detail) { return checks.push({ label: label, pass: pass, detail: detail }); };
70
+ _b.label = 1;
71
+ case 1:
72
+ _b.trys.push([1, , 20, 25]);
73
+ return [4 /*yield*/, (0, setup_isolated_1.setup_isolated_tenant)({
74
+ name: "stripe items ".concat(RAND()),
75
+ users: [{ isAdmin: true }],
76
+ apiKeys: 0,
77
+ })];
78
+ case 2:
79
+ tenant = _b.sent();
80
+ sdk_2 = tenant.sdk, businessId = tenant.businessId;
81
+ return [4 /*yield*/, sdk_2.api.products.createOne({
82
+ title: "Checkout Item Product ".concat(RAND()),
83
+ cost: { amount: 500, currency: 'USD' },
84
+ processor: 'Stripe',
85
+ })];
86
+ case 3:
87
+ product = _b.sent();
88
+ return [4 /*yield*/, sdk_2.api.endusers.createOne({ email: "items_".concat(RAND(), "@test.tellescope.com") })];
89
+ case 4:
90
+ enduser = _b.sent();
91
+ enduserSDK_1 = new sdk_1.EnduserSession({ host: host, businessId: businessId });
92
+ return [4 /*yield*/, (0, setup_1.authenticate_enduser_via_token)(sdk_2, enduserSDK_1, { id: enduser.id })
93
+ // returns the error message for a rejected request, or '' when the request succeeded
94
+ ];
95
+ case 5:
96
+ _b.sent();
97
+ errorFor = function (session, args) { return __awaiter(void 0, void 0, void 0, function () {
98
+ var err_1;
99
+ var _a;
100
+ return __generator(this, function (_b) {
101
+ switch (_b.label) {
102
+ case 0:
103
+ _b.trys.push([0, 2, , 3]);
104
+ return [4 /*yield*/, session.api.products.prepare_stripe_checkout(args)];
105
+ case 1:
106
+ _b.sent();
107
+ return [2 /*return*/, ''];
108
+ case 2:
109
+ err_1 = _b.sent();
110
+ return [2 /*return*/, typeof err_1 === 'string' ? err_1 : ((_a = err_1 === null || err_1 === void 0 ? void 0 : err_1.message) !== null && _a !== void 0 ? _a : JSON.stringify(err_1))];
111
+ case 3: return [2 /*return*/];
112
+ }
113
+ });
114
+ }); };
115
+ return [4 /*yield*/, errorFor(enduserSDK_1, { items: [{ stripePriceId: 'price_raw', quantity: 1 }] })];
116
+ case 6:
117
+ enduserRawPrice = _b.sent();
118
+ record('an enduser naming a raw stripePriceId is rejected', enduserRawPrice === ENDUSER_RAW_IDS_MESSAGE, "expected \"".concat(ENDUSER_RAW_IDS_MESSAGE, "\", got \"").concat(enduserRawPrice, "\""));
119
+ return [4 /*yield*/, errorFor(enduserSDK_1, { items: [{ subscriptionPriceId: 'price_sub', quantity: 1 }] })];
120
+ case 7:
121
+ enduserRawSubscription = _b.sent();
122
+ record('an enduser naming a raw subscriptionPriceId is rejected', enduserRawSubscription === ENDUSER_RAW_IDS_MESSAGE, "expected \"".concat(ENDUSER_RAW_IDS_MESSAGE, "\", got \"").concat(enduserRawSubscription, "\""));
123
+ return [4 /*yield*/, errorFor(enduserSDK_1, { items: [{ productId: product.id, quantity: 2 }] })];
124
+ case 8:
125
+ enduserProduct = _b.sent();
126
+ record('an enduser naming a Tellescope productId is allowed through the guard', enduserProduct === NO_STRIPE_MESSAGE, "expected \"".concat(NO_STRIPE_MESSAGE, "\", got \"").concat(enduserProduct, "\""));
127
+ return [4 /*yield*/, errorFor(sdk_2, { items: [{ stripePriceId: 'price_raw', quantity: 1 }] })];
128
+ case 9:
129
+ userRawPrice = _b.sent();
130
+ record('a user session naming a raw stripePriceId is allowed through the guard', userRawPrice === NO_STRIPE_MESSAGE, "expected \"".concat(NO_STRIPE_MESSAGE, "\", got \"").concat(userRawPrice, "\""));
131
+ return [4 /*yield*/, errorFor(sdk_2, { productIds: [product.id], items: [{ productId: product.id, quantity: 1 }] })];
132
+ case 10:
133
+ both = _b.sent();
134
+ record('passing both productIds and items is rejected', both === "Pass either productIds or items, not both", "expected the \"not both\" message, got \"".concat(both, "\""));
135
+ return [4 /*yield*/, errorFor(sdk_2, {})];
136
+ case 11:
137
+ neither = _b.sent();
138
+ record('passing neither productIds nor items is rejected', neither === "One of productIds or items is required", "expected the \"one of\" message, got \"".concat(neither, "\""));
139
+ return [4 /*yield*/, errorFor(sdk_2, { items: [{ quantity: 1 }] })];
140
+ case 12:
141
+ noId = _b.sent();
142
+ record('an items entry naming no id is rejected', noId === EXACTLY_ONE_ID_MESSAGE, "expected the \"exactly one\" message, got \"".concat(noId, "\""));
143
+ return [4 /*yield*/, errorFor(sdk_2, { items: [{ productId: product.id, stripePriceId: 'price_raw', quantity: 1 }] })];
144
+ case 13:
145
+ twoIds = _b.sent();
146
+ record('an items entry naming two ids is rejected', twoIds === EXACTLY_ONE_ID_MESSAGE, "expected the \"exactly one\" message, got \"".concat(twoIds, "\""));
147
+ unknownId = '60398b0231a295e64f084fd9';
148
+ return [4 /*yield*/, errorFor(sdk_2, { items: [{ productId: unknownId, quantity: 1 }] })];
149
+ case 14:
150
+ unknown = _b.sent();
151
+ record('an items entry naming an unknown productId is rejected, naming the id', unknown.includes(unknownId) && unknown.includes('No product found'), "expected a \"No product found for productId ".concat(unknownId, "\", got \"").concat(unknown, "\""));
152
+ _i = 0, _a = [0, -1, 1.5];
153
+ _b.label = 15;
154
+ case 15:
155
+ if (!(_i < _a.length)) return [3 /*break*/, 18];
156
+ quantity = _a[_i];
157
+ return [4 /*yield*/, errorFor(sdk_2, { items: [{ productId: product.id, quantity: quantity }] })];
158
+ case 16:
159
+ message = _b.sent();
160
+ record("quantity ".concat(quantity, " is rejected"), message.includes(QUANTITY_MESSAGE), "expected a message containing \"".concat(QUANTITY_MESSAGE, "\", got \"").concat(message, "\""));
161
+ _b.label = 17;
162
+ case 17:
163
+ _i++;
164
+ return [3 /*break*/, 15];
165
+ case 18: return [4 /*yield*/, errorFor(sdk_2, { productIds: [product.id] })];
166
+ case 19:
167
+ productIdsOnly = _b.sent();
168
+ record('a productIds-only request is unaffected', productIdsOnly === NO_STRIPE_MESSAGE, "expected \"".concat(NO_STRIPE_MESSAGE, "\", got \"").concat(productIdsOnly, "\""));
169
+ return [3 /*break*/, 25];
170
+ case 20:
171
+ if (!tenant) return [3 /*break*/, 24];
172
+ _b.label = 21;
173
+ case 21:
174
+ _b.trys.push([21, 23, , 24]);
175
+ return [4 /*yield*/, tenant.teardown()];
176
+ case 22:
177
+ _b.sent();
178
+ return [3 /*break*/, 24];
179
+ case 23:
180
+ e_1 = _b.sent();
181
+ console.error('teardown failed', e_1);
182
+ return [3 /*break*/, 24];
183
+ case 24: return [7 /*endfinally*/];
184
+ case 25: return [2 /*return*/, checks];
185
+ }
186
+ });
187
+ }); };
188
+ exports.stripe_checkout_items_checks = stripe_checkout_items_checks;
189
+ var stripe_checkout_items_tests = function () { return __awaiter(void 0, void 0, void 0, function () {
190
+ var _a, _b;
191
+ return __generator(this, function (_c) {
192
+ switch (_c.label) {
193
+ case 0:
194
+ _a = setup_isolated_1.assert_checks;
195
+ _b = ['prepare_stripe_checkout items guards'];
196
+ return [4 /*yield*/, (0, exports.stripe_checkout_items_checks)()];
197
+ case 1:
198
+ _a.apply(void 0, _b.concat([_c.sent()]));
199
+ return [2 /*return*/];
200
+ }
201
+ });
202
+ }); };
203
+ exports.stripe_checkout_items_tests = stripe_checkout_items_tests;
204
+ /** Enrolls this suite in the auto-discovered parallel pool — see run_discovered_isolated_suites. */
205
+ exports.ISOLATED_SUITE = { name: 'prepare_stripe_checkout items guards', run: exports.stripe_checkout_items_checks };
206
+ // Allow running this test file independently
207
+ if (require.main === module) {
208
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
209
+ (0, exports.stripe_checkout_items_tests)()
210
+ .then(function () {
211
+ console.log("✅ stripe_checkout_items test suite completed successfully");
212
+ process.exit(0);
213
+ })
214
+ .catch(function (error) {
215
+ console.error("❌ stripe_checkout_items test suite failed:", error);
216
+ process.exit(1);
217
+ });
218
+ }
219
+ //# sourceMappingURL=stripe_checkout_items.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe_checkout_items.test.js","sourceRoot":"","sources":["../../../../src/tests/api_tests/stripe_checkout_items.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import { Check } from "../setup_isolated";
2
+ export declare const stripe_multiple_customer_ids_checks: () => Promise<Check[]>;
3
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
4
+ export declare const stripe_multiple_customer_ids_tests: () => Promise<void>;
5
+ //# sourceMappingURL=stripe_multiple_customer_ids.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe_multiple_customer_ids.test.d.ts","sourceRoot":"","sources":["../../../../src/tests/api_tests/stripe_multiple_customer_ids.test.ts"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,EAEN,MAAM,mBAAmB,CAAA;AAiD1B,eAAO,MAAM,mCAAmC,QAAa,QAAQ,KAAK,EAAE,CAuR3E,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,kCAAkC,qBAE9C,CAAA"}
@@ -0,0 +1,396 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.stripe_multiple_customer_ids_tests = exports.stripe_multiple_customer_ids_checks = void 0;
43
+ require('source-map-support').install();
44
+ var enduser_1 = require("../../enduser");
45
+ var setup_isolated_1 = require("../setup_isolated");
46
+ var stripe_webhook_helpers_1 = require("../stripe_webhook_helpers");
47
+ var crypto_1 = __importDefault(require("crypto"));
48
+ var host = process.env.API_URL || 'http://localhost:8080';
49
+ var RAND = function () { return Math.random().toString(36).slice(2, 10); };
50
+ var ENDUSER_PASSWORD = 'StripeMulti!Password1';
51
+ var KEY_A = 'pk_test_stripe_account_a';
52
+ var KEY_B = 'pk_test_stripe_account_b';
53
+ var poll_for = function (get, done, attempts) {
54
+ if (attempts === void 0) { attempts = 20; }
55
+ return __awaiter(void 0, void 0, void 0, function () {
56
+ var value, i;
57
+ return __generator(this, function (_a) {
58
+ switch (_a.label) {
59
+ case 0: return [4 /*yield*/, get()];
60
+ case 1:
61
+ value = _a.sent();
62
+ i = 0;
63
+ _a.label = 2;
64
+ case 2:
65
+ if (!(i < attempts && !done(value))) return [3 /*break*/, 6];
66
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 250); })];
67
+ case 3:
68
+ _a.sent();
69
+ return [4 /*yield*/, get()];
70
+ case 4:
71
+ value = _a.sent();
72
+ _a.label = 5;
73
+ case 5:
74
+ i++;
75
+ return [3 /*break*/, 2];
76
+ case 6: return [2 /*return*/, value];
77
+ }
78
+ });
79
+ });
80
+ };
81
+ var id_for = function (entries, stripeKey) {
82
+ var _a;
83
+ return ((_a = (entries !== null && entries !== void 0 ? entries : []).find(function (e) { return e.stripeKey === stripeKey; })) === null || _a === void 0 ? void 0 : _a.customerId);
84
+ };
85
+ var stripe_multiple_customer_ids_checks = function () { return __awaiter(void 0, void 0, void 0, function () {
86
+ var checks, record, tenant, sdk_1, businessId, enduserEmail, enduser_2, entries_after, afterA, afterB, afterCorrection, afterDirect, destination, source_1, sourceEntries, merged, sourceOnly, destinationOnly, _a, _b, mergedUnion, stripe_integration, accountA, accountB, webhookEnduser_1, purchases_for, secondAccountWebhook, secondAccountPurchases, wrongAccountWebhook, afterWrongAccount, duplicateCustomerWebhook, purchasesAfterDuplicate, afterDuplicate, enduserSDK, selfRead, selfWrite, afterSelfWrite, e_1;
87
+ var _c;
88
+ return __generator(this, function (_d) {
89
+ switch (_d.label) {
90
+ case 0:
91
+ checks = [];
92
+ record = function (label, pass, detail) { return checks.push({ label: label, pass: pass, detail: detail }); };
93
+ _d.label = 1;
94
+ case 1:
95
+ _d.trys.push([1, , 43, 48]);
96
+ return [4 /*yield*/, (0, setup_isolated_1.setup_isolated_tenant)({
97
+ name: "StripeMultiCustomer ".concat(RAND()),
98
+ users: [{ isAdmin: true }],
99
+ apiKeys: 0,
100
+ })];
101
+ case 2:
102
+ tenant = _d.sent();
103
+ sdk_1 = tenant.sdk, businessId = tenant.businessId;
104
+ enduserEmail = "stripe-multi-".concat(RAND(), "@example.com");
105
+ return [4 /*yield*/, sdk_1.api.endusers.createOne({ email: enduserEmail, fname: 'Stripe', lname: 'Multi' })];
106
+ case 3:
107
+ enduser_2 = _d.sent();
108
+ entries_after = function (expect) { return poll_for(function () { return sdk_1.api.endusers.getOne(enduser_2.id).then(function (e) { var _a; return ((_a = e.stripeCustomerIds) !== null && _a !== void 0 ? _a : []); }); }, expect); };
109
+ /* ===== A: a pair write is mirrored into the per-account list ===== */
110
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(enduser_2.id, { stripeKey: KEY_A, stripeCustomerId: 'cus_account_a' })];
111
+ case 4:
112
+ /* ===== A: a pair write is mirrored into the per-account list ===== */
113
+ _d.sent();
114
+ return [4 /*yield*/, entries_after(function (e) { return id_for(e, KEY_A) === 'cus_account_a'; })];
115
+ case 5:
116
+ afterA = _d.sent();
117
+ record('A1: a staff write of the pair is mirrored into stripeCustomerIds', id_for(afterA, KEY_A) === 'cus_account_a' && afterA.length === 1, "entries=".concat(JSON.stringify(afterA), " (want one entry for ").concat(KEY_A, " -> cus_account_a)"));
118
+ /* ===== A/B: a second account is added, and the pair does not move ===== */
119
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(enduser_2.id, { stripeKey: KEY_B, stripeCustomerId: 'cus_account_b' })];
120
+ case 6:
121
+ /* ===== A/B: a second account is added, and the pair does not move ===== */
122
+ _d.sent();
123
+ return [4 /*yield*/, entries_after(function (e) { return !!id_for(e, KEY_B); })];
124
+ case 7:
125
+ afterB = _d.sent();
126
+ record('A2: linking a second Stripe account adds an entry and keeps the first', id_for(afterB, KEY_A) === 'cus_account_a' && id_for(afterB, KEY_B) === 'cus_account_b' && afterB.length === 2, "entries=".concat(JSON.stringify(afterB), " (want both accounts; losing ").concat(KEY_A, " is the clobber this feature removes)"));
127
+ /* ===== C: the same account can be corrected in place ===== */
128
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(enduser_2.id, { stripeKey: KEY_A, stripeCustomerId: 'cus_account_a_corrected' })];
129
+ case 8:
130
+ /* ===== C: the same account can be corrected in place ===== */
131
+ _d.sent();
132
+ return [4 /*yield*/, entries_after(function (e) { return id_for(e, KEY_A) === 'cus_account_a_corrected'; })];
133
+ case 9:
134
+ afterCorrection = _d.sent();
135
+ record('C1: re-saving an account replaces its entry rather than duplicating it', id_for(afterCorrection, KEY_A) === 'cus_account_a_corrected'
136
+ && id_for(afterCorrection, KEY_B) === 'cus_account_b'
137
+ && afterCorrection.length === 2, "entries=".concat(JSON.stringify(afterCorrection)));
138
+ // direct staff write of the list (manual correction / API clients)
139
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(enduser_2.id, { stripeCustomerIds: [{ stripeKey: KEY_A, customerId: 'cus_direct_a' }] }, { replaceObjectFields: true })];
140
+ case 10:
141
+ // direct staff write of the list (manual correction / API clients)
142
+ _d.sent();
143
+ return [4 /*yield*/, sdk_1.api.endusers.getOne(enduser_2.id)];
144
+ case 11:
145
+ afterDirect = _d.sent();
146
+ record('C2: staff can write stripeCustomerIds directly', id_for(afterDirect.stripeCustomerIds, KEY_A) === 'cus_direct_a', "entries=".concat(JSON.stringify(afterDirect.stripeCustomerIds)));
147
+ return [4 /*yield*/, sdk_1.api.endusers.createOne({
148
+ email: "stripe-multi-dest-".concat(RAND(), "@example.com"), fname: 'Stripe', lname: 'Destination',
149
+ })];
150
+ case 12:
151
+ destination = _d.sent();
152
+ return [4 /*yield*/, sdk_1.api.endusers.createOne({
153
+ email: "stripe-multi-src-".concat(RAND(), "@example.com"), fname: 'Stripe', lname: 'Source',
154
+ })];
155
+ case 13:
156
+ source_1 = _d.sent();
157
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(destination.id, { stripeCustomerIds: [{ stripeKey: KEY_A, customerId: 'cus_destination_a' }, { stripeKey: KEY_B, customerId: 'cus_destination_b' }] }, { replaceObjectFields: true })];
158
+ case 14:
159
+ _d.sent();
160
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(source_1.id, { stripeKey: KEY_B, stripeCustomerId: 'cus_source_b' })];
161
+ case 15:
162
+ _d.sent();
163
+ return [4 /*yield*/, poll_for(function () { return sdk_1.api.endusers.getOne(source_1.id).then(function (e) { var _a; return ((_a = e.stripeCustomerIds) !== null && _a !== void 0 ? _a : []); }); }, function (e) { return !!id_for(e, KEY_B); })];
164
+ case 16:
165
+ sourceEntries = _d.sent();
166
+ return [4 /*yield*/, sdk_1.api.endusers.merge({ sourceEnduserId: source_1.id, destinationEnduserId: destination.id })];
167
+ case 17:
168
+ _d.sent();
169
+ return [4 /*yield*/, sdk_1.api.endusers.getOne(destination.id)];
170
+ case 18:
171
+ merged = (_d.sent()).stripeCustomerIds;
172
+ record('D1: merge keeps the destination id for an account both patients were linked to', id_for(merged, KEY_B) === 'cus_destination_b' && id_for(merged, KEY_A) === 'cus_destination_a'
173
+ && (merged !== null && merged !== void 0 ? merged : []).length === 2, "merged=".concat(JSON.stringify(merged), " source had ").concat(JSON.stringify(sourceEntries)));
174
+ return [4 /*yield*/, sdk_1.api.endusers.createOne({
175
+ email: "stripe-multi-src2-".concat(RAND(), "@example.com"), fname: 'Stripe', lname: 'SourceOnly',
176
+ stripeKey: KEY_B, stripeCustomerId: 'cus_source_only_b',
177
+ })];
178
+ case 19:
179
+ sourceOnly = _d.sent();
180
+ return [4 /*yield*/, sdk_1.api.endusers.createOne({
181
+ email: "stripe-multi-dest2-".concat(RAND(), "@example.com"), fname: 'Stripe', lname: 'DestinationOnly',
182
+ stripeKey: KEY_A, stripeCustomerId: 'cus_dest_only_a',
183
+ })];
184
+ case 20:
185
+ destinationOnly = _d.sent();
186
+ _a = record;
187
+ _b = ['D2 setup: a patient created with the pair alone carries no per-account list'];
188
+ return [4 /*yield*/, sdk_1.api.endusers.getOne(sourceOnly.id)];
189
+ case 21:
190
+ _a.apply(void 0, _b.concat([!((_c = (_d.sent()).stripeCustomerIds) !== null && _c !== void 0 ? _c : []).length,
191
+ 'the legacy shape this merge has to cope with']));
192
+ return [4 /*yield*/, sdk_1.api.endusers.merge({ sourceEnduserId: sourceOnly.id, destinationEnduserId: destinationOnly.id })];
193
+ case 22:
194
+ _d.sent();
195
+ return [4 /*yield*/, sdk_1.api.endusers.getOne(destinationOnly.id)];
196
+ case 23:
197
+ mergedUnion = (_d.sent()).stripeCustomerIds;
198
+ record('D3: merging two legacy-pair patients lists both accounts on the survivor', id_for(mergedUnion, KEY_A) === 'cus_dest_only_a' && id_for(mergedUnion, KEY_B) === 'cus_source_only_b', "merged=".concat(JSON.stringify(mergedUnion), " (the survivor must be chargeable on both accounts)"));
199
+ stripe_integration = function (sdkSession) { return __awaiter(void 0, void 0, void 0, function () {
200
+ var stripePublicKey, webhooksSecret;
201
+ return __generator(this, function (_a) {
202
+ switch (_a.label) {
203
+ case 0:
204
+ stripePublicKey = "pk_test_multi_".concat(Date.now(), "_").concat(Math.floor(Math.random() * 1e6));
205
+ webhooksSecret = "whsec_test_".concat(crypto_1.default.randomBytes(16).toString('hex'));
206
+ return [4 /*yield*/, sdkSession.api.integrations.createOne({
207
+ title: 'Stripe',
208
+ environment: stripe_webhook_helpers_1.TEST_STRIPE_ENVIRONMENT,
209
+ authentication: {
210
+ type: 'apiKey',
211
+ info: {
212
+ access_token: 'sk_test_unused_by_this_path',
213
+ refresh_token: 'unused',
214
+ scope: '',
215
+ expiry_date: Date.now(),
216
+ token_type: 'Bearer',
217
+ external_id: stripePublicKey,
218
+ },
219
+ },
220
+ webhooksSecret: webhooksSecret,
221
+ })];
222
+ case 1:
223
+ _a.sent();
224
+ return [2 /*return*/, { stripePublicKey: stripePublicKey, webhooksSecret: webhooksSecret }];
225
+ }
226
+ });
227
+ }); };
228
+ return [4 /*yield*/, stripe_integration(sdk_1)];
229
+ case 24:
230
+ accountA = _d.sent();
231
+ return [4 /*yield*/, stripe_integration(sdk_1)];
232
+ case 25:
233
+ accountB = _d.sent();
234
+ return [4 /*yield*/, sdk_1.api.endusers.createOne({
235
+ email: "stripe-multi-webhook-".concat(RAND(), "@example.com"), fname: 'Stripe', lname: 'Webhook',
236
+ })
237
+ // linked to account A by the pinned pair, and to account B only through the per-account list —
238
+ // exactly the shape a patient ends up in after buying on a second Stripe account
239
+ ];
240
+ case 26:
241
+ webhookEnduser_1 = _d.sent();
242
+ // linked to account A by the pinned pair, and to account B only through the per-account list —
243
+ // exactly the shape a patient ends up in after buying on a second Stripe account
244
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(webhookEnduser_1.id, { stripeKey: accountA.stripePublicKey, stripeCustomerId: 'cus_webhook_a' })];
245
+ case 27:
246
+ // linked to account A by the pinned pair, and to account B only through the per-account list —
247
+ // exactly the shape a patient ends up in after buying on a second Stripe account
248
+ _d.sent();
249
+ return [4 /*yield*/, sdk_1.api.endusers.updateOne(webhookEnduser_1.id, { stripeCustomerIds: [
250
+ { stripeKey: accountA.stripePublicKey, customerId: 'cus_webhook_a' },
251
+ { stripeKey: accountB.stripePublicKey, customerId: 'cus_webhook_b' },
252
+ ] }, { replaceObjectFields: true })];
253
+ case 28:
254
+ _d.sent();
255
+ purchases_for = function () { return sdk_1.api.purchases.getSome({ filter: { enduserId: webhookEnduser_1.id } }); };
256
+ return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
257
+ stripePublicKey: accountB.stripePublicKey,
258
+ secret: accountB.webhooksSecret,
259
+ event: (0, stripe_webhook_helpers_1.invoice_paid_event)({ stripeCustomerId: 'cus_webhook_b' }),
260
+ })];
261
+ case 29:
262
+ secondAccountWebhook = _d.sent();
263
+ return [4 /*yield*/, poll_for(purchases_for, function (p) { return p.length > 0; })];
264
+ case 30:
265
+ secondAccountPurchases = _d.sent();
266
+ record('F1: an invoice.paid from the second account is attributed to the patient linked to it', secondAccountWebhook.status === 200 && secondAccountPurchases.length === 1, "webhook=".concat(secondAccountWebhook.status, " purchases=").concat(secondAccountPurchases.length)
267
+ + ' — 0 means the lookup only read the pinned pair, so the second account matches no patient');
268
+ return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
269
+ stripePublicKey: accountA.stripePublicKey,
270
+ secret: accountA.webhooksSecret,
271
+ event: (0, stripe_webhook_helpers_1.invoice_paid_event)({ stripeCustomerId: 'cus_webhook_b' }),
272
+ })];
273
+ case 31:
274
+ wrongAccountWebhook = _d.sent();
275
+ return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 1500); })];
276
+ case 32:
277
+ _d.sent();
278
+ return [4 /*yield*/, purchases_for()];
279
+ case 33:
280
+ afterWrongAccount = _d.sent();
281
+ record('F2: an id belonging to another account does not resolve through this one', wrongAccountWebhook.status === 200 && afterWrongAccount.length === 1, "webhook=".concat(wrongAccountWebhook.status, " purchases=").concat(afterWrongAccount.length, " (want the 1 from F1)"));
282
+ return [4 /*yield*/, (0, stripe_webhook_helpers_1.post_stripe_webhook)({
283
+ stripePublicKey: accountB.stripePublicKey,
284
+ secret: accountB.webhooksSecret,
285
+ event: {
286
+ type: 'invoice.paid',
287
+ data: {
288
+ object: {
289
+ id: "in_test_dupe_".concat(Date.now()),
290
+ customer: 'cus_webhook_b_duplicate',
291
+ customer_email: webhookEnduser_1.email,
292
+ amount_paid: 1999,
293
+ lines: { object: 'list', data: [], has_more: false },
294
+ discount: null,
295
+ discounts: [],
296
+ },
297
+ },
298
+ },
299
+ })
300
+ // the second purchase is what proves the handler REACHED the linkage write: it means the email
301
+ // fallback matched this patient, so "the entry did not move" is a decision and not a no-op
302
+ ];
303
+ case 34:
304
+ duplicateCustomerWebhook = _d.sent();
305
+ return [4 /*yield*/, poll_for(purchases_for, function (p) { return p.length > 1; })];
306
+ case 35:
307
+ purchasesAfterDuplicate = _d.sent();
308
+ return [4 /*yield*/, poll_for(function () { return sdk_1.api.endusers.getOne(webhookEnduser_1.id).then(function (e) { var _a; return ((_a = e.stripeCustomerIds) !== null && _a !== void 0 ? _a : []); }); }, function () { return false; }, // always poll to the end, so a late write would be seen
309
+ 6)];
310
+ case 36:
311
+ afterDuplicate = _d.sent();
312
+ record('F3: a webhook for a different customer on an already-linked account does not repoint it', duplicateCustomerWebhook.status === 200
313
+ && purchasesAfterDuplicate.length === 2
314
+ && id_for(afterDuplicate, accountB.stripePublicKey) === 'cus_webhook_b', "webhook=".concat(duplicateCustomerWebhook.status, " purchases=").concat(purchasesAfterDuplicate.length, " (want 2,")
315
+ + " so the email fallback matched and the linkage write was reached) entries=".concat(JSON.stringify(afterDuplicate)));
316
+ /* ===== E: never visible or writable from a patient session ===== */
317
+ return [4 /*yield*/, sdk_1.api.endusers.set_password({ id: enduser_2.id, password: ENDUSER_PASSWORD })];
318
+ case 37:
319
+ /* ===== E: never visible or writable from a patient session ===== */
320
+ _d.sent();
321
+ enduserSDK = new enduser_1.EnduserSession({ host: host, businessId: businessId });
322
+ return [4 /*yield*/, enduserSDK.authenticate(enduserEmail, ENDUSER_PASSWORD)];
323
+ case 38:
324
+ _d.sent();
325
+ return [4 /*yield*/, enduserSDK.api.endusers.getOne(enduser_2.id)];
326
+ case 39:
327
+ selfRead = _d.sent();
328
+ record('E1: a model read from a patient session is redacted', !selfRead.stripeCustomerIds && !selfRead.stripeCustomerId, "enduser read back stripeCustomerIds=".concat(JSON.stringify(selfRead.stripeCustomerIds))
329
+ + " stripeCustomerId=".concat(JSON.stringify(selfRead.stripeCustomerId)));
330
+ // The login / refresh payloads return the enduser record itself, NOT a schema-redacted model
331
+ // read, so this is a separate exposure path — it is how the portal receives its own record, and
332
+ // how stripeCustomerId (which the portal's Manage Subscriptions button needs) reaches it.
333
+ record('E1b: the login payload the portal receives carries no per-account customer ids', !enduserSDK.userInfo.stripeCustomerIds, "login payload stripeCustomerIds=".concat(JSON.stringify(enduserSDK.userInfo.stripeCustomerIds))
334
+ + " (stripeCustomerId=".concat(JSON.stringify(enduserSDK.userInfo.stripeCustomerId), " stays, the portal reads it)"));
335
+ return [4 /*yield*/, enduserSDK.refresh_session()];
336
+ case 40:
337
+ _d.sent();
338
+ record('E1c: the refresh_session payload carries no per-account customer ids', !enduserSDK.userInfo.stripeCustomerIds, "refreshed payload stripeCustomerIds=".concat(JSON.stringify(enduserSDK.userInfo.stripeCustomerIds)));
339
+ return [4 /*yield*/, enduserSDK.api.endusers.updateOne(enduser_2.id, { stripeCustomerIds: [{ stripeKey: KEY_A, customerId: 'cus_attacker' }] }).then(function () { return ({ resolved: true }); }).catch(function (e) { return ({ rejected: (e === null || e === void 0 ? void 0 : e.message) || String(e) }); })];
340
+ case 41:
341
+ selfWrite = _d.sent();
342
+ return [4 /*yield*/, sdk_1.api.endusers.getOne(enduser_2.id)];
343
+ case 42:
344
+ afterSelfWrite = _d.sent();
345
+ record('E2: a patient cannot write the per-account customer ids', selfWrite.rejected === 'stripeCustomerIds cannot be updated by endusers'
346
+ && id_for(afterSelfWrite.stripeCustomerIds, KEY_A) === 'cus_direct_a', "result=".concat(JSON.stringify(selfWrite), " entries=").concat(JSON.stringify(afterSelfWrite.stripeCustomerIds)));
347
+ return [3 /*break*/, 48];
348
+ case 43:
349
+ if (!tenant) return [3 /*break*/, 47];
350
+ _d.label = 44;
351
+ case 44:
352
+ _d.trys.push([44, 46, , 47]);
353
+ return [4 /*yield*/, tenant.teardown()];
354
+ case 45:
355
+ _d.sent();
356
+ return [3 /*break*/, 47];
357
+ case 46:
358
+ e_1 = _d.sent();
359
+ console.error('teardown failed', e_1);
360
+ return [3 /*break*/, 47];
361
+ case 47: return [7 /*endfinally*/];
362
+ case 48: return [2 /*return*/, checks];
363
+ }
364
+ });
365
+ }); };
366
+ exports.stripe_multiple_customer_ids_checks = stripe_multiple_customer_ids_checks;
367
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
368
+ var stripe_multiple_customer_ids_tests = function () { return __awaiter(void 0, void 0, void 0, function () {
369
+ var _a, _b;
370
+ return __generator(this, function (_c) {
371
+ switch (_c.label) {
372
+ case 0:
373
+ _a = setup_isolated_1.assert_checks;
374
+ _b = ['Stripe multiple customer ids'];
375
+ return [4 /*yield*/, (0, exports.stripe_multiple_customer_ids_checks)()];
376
+ case 1:
377
+ _a.apply(void 0, _b.concat([_c.sent()]));
378
+ return [2 /*return*/];
379
+ }
380
+ });
381
+ }); };
382
+ exports.stripe_multiple_customer_ids_tests = stripe_multiple_customer_ids_tests;
383
+ // Allow running this test file independently
384
+ if (require.main === module) {
385
+ console.log("\uD83C\uDF10 Using API URL: ".concat(host));
386
+ (0, exports.stripe_multiple_customer_ids_tests)()
387
+ .then(function () {
388
+ console.log("✅ Stripe multiple customer ids test suite completed successfully");
389
+ process.exit(0);
390
+ })
391
+ .catch(function (error) {
392
+ console.error("❌ Stripe multiple customer ids test suite failed:", error);
393
+ process.exit(1);
394
+ });
395
+ }
396
+ //# sourceMappingURL=stripe_multiple_customer_ids.test.js.map