@tellescope/sdk 2.0.0-beta.0 → 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 (303) hide show
  1. package/CLAUDE.md +24 -0
  2. package/env.example +6 -0
  3. package/lib/cjs/sdk.d.ts +376 -3
  4. package/lib/cjs/sdk.d.ts.map +1 -1
  5. package/lib/cjs/sdk.js +1 -0
  6. package/lib/cjs/sdk.js.map +1 -1
  7. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts +22 -0
  8. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts.map +1 -0
  9. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.js +506 -0
  10. package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.js.map +1 -0
  11. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts +3 -1
  12. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -1
  13. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js +3 -1
  14. package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js.map +1 -1
  15. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts +3 -1
  16. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -1
  17. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js +3 -1
  18. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -1
  19. package/lib/cjs/tests/api_tests/form_vital_reading.test.d.ts +7 -0
  20. package/lib/cjs/tests/api_tests/form_vital_reading.test.d.ts.map +1 -0
  21. package/lib/cjs/tests/api_tests/form_vital_reading.test.js +461 -0
  22. package/lib/cjs/tests/api_tests/form_vital_reading.test.js.map +1 -0
  23. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.d.ts +26 -0
  24. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.d.ts.map +1 -0
  25. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.js +232 -0
  26. package/lib/cjs/tests/api_tests/journey_statistics_cache.test.js.map +1 -0
  27. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.d.ts +34 -0
  28. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.d.ts.map +1 -0
  29. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.js +243 -0
  30. package/lib/cjs/tests/api_tests/journey_step_manual_count.test.js.map +1 -0
  31. package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts +3 -1
  32. package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -1
  33. package/lib/cjs/tests/api_tests/load_threads_care_team.test.js +3 -1
  34. package/lib/cjs/tests/api_tests/load_threads_care_team.test.js.map +1 -1
  35. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
  36. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js +139 -9
  37. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
  38. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +3 -1
  39. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -1
  40. package/lib/cjs/tests/api_tests/organization_signup.test.js +3 -1
  41. package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -1
  42. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.d.ts +7 -0
  43. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.d.ts.map +1 -0
  44. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.js +571 -0
  45. package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.js.map +1 -0
  46. package/lib/cjs/tests/api_tests/programs.test.d.ts +3 -1
  47. package/lib/cjs/tests/api_tests/programs.test.d.ts.map +1 -1
  48. package/lib/cjs/tests/api_tests/programs.test.js +73 -17
  49. package/lib/cjs/tests/api_tests/programs.test.js.map +1 -1
  50. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.d.ts +7 -0
  51. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.d.ts.map +1 -0
  52. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.js +733 -0
  53. package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.js.map +1 -0
  54. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +3 -1
  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 +58 -48
  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/appointment-reschedule-ownership.test.d.ts +3 -1
  59. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -1
  60. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js +3 -1
  61. package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -1
  62. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.d.ts +3 -1
  63. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.d.ts.map +1 -1
  64. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.js +3 -1
  65. package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.js.map +1 -1
  66. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts +3 -1
  67. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -1
  68. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js +3 -1
  69. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -1
  70. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -1
  71. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +424 -39
  72. package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -1
  73. package/lib/cjs/tests/api_tests/security/register_business_id_injection.test.js +2 -2
  74. package/lib/cjs/tests/api_tests/security/register_business_id_injection.test.js.map +1 -1
  75. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +3 -1
  76. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -1
  77. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +3 -1
  78. package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -1
  79. package/lib/cjs/tests/api_tests/session_for_join_link.test.d.ts +10 -0
  80. package/lib/cjs/tests/api_tests/session_for_join_link.test.d.ts.map +1 -0
  81. package/lib/cjs/tests/api_tests/session_for_join_link.test.js +147 -0
  82. package/lib/cjs/tests/api_tests/session_for_join_link.test.js.map +1 -0
  83. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -1
  84. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js +191 -88
  85. package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js.map +1 -1
  86. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.d.ts +17 -0
  87. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.d.ts.map +1 -0
  88. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.js +219 -0
  89. package/lib/cjs/tests/api_tests/stripe_checkout_items.test.js.map +1 -0
  90. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.d.ts +5 -0
  91. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.d.ts.map +1 -0
  92. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.js +396 -0
  93. package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.js.map +1 -0
  94. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +3 -1
  95. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -1
  96. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +3 -1
  97. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -1
  98. package/lib/cjs/tests/api_tests/totp_mfa.test.d.ts.map +1 -1
  99. package/lib/cjs/tests/api_tests/totp_mfa.test.js +37 -31
  100. package/lib/cjs/tests/api_tests/totp_mfa.test.js.map +1 -1
  101. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts +3 -1
  102. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -1
  103. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js +3 -1
  104. package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js.map +1 -1
  105. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts +3 -1
  106. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -1
  107. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js +3 -1
  108. package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -1
  109. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts +3 -1
  110. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -1
  111. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js +3 -1
  112. package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js.map +1 -1
  113. package/lib/cjs/tests/api_tests/vitl_push.test.d.ts +7 -0
  114. package/lib/cjs/tests/api_tests/vitl_push.test.d.ts.map +1 -0
  115. package/lib/cjs/tests/api_tests/vitl_push.test.js +341 -0
  116. package/lib/cjs/tests/api_tests/vitl_push.test.js.map +1 -0
  117. package/lib/cjs/tests/setup_isolated.d.ts +48 -0
  118. package/lib/cjs/tests/setup_isolated.d.ts.map +1 -1
  119. package/lib/cjs/tests/setup_isolated.js +119 -1
  120. package/lib/cjs/tests/setup_isolated.js.map +1 -1
  121. package/lib/cjs/tests/tests.d.ts.map +1 -1
  122. package/lib/cjs/tests/tests.js +378 -282
  123. package/lib/cjs/tests/tests.js.map +1 -1
  124. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.d.ts +2 -0
  125. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.d.ts.map +1 -0
  126. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.js +201 -0
  127. package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.js.map +1 -0
  128. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.d.ts +3 -0
  129. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.d.ts.map +1 -0
  130. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.js +120 -0
  131. package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.js.map +1 -0
  132. package/lib/cjs/tests/webhooks_tests.js +44 -34
  133. package/lib/cjs/tests/webhooks_tests.js.map +1 -1
  134. package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  135. package/lib/esm/sdk.d.ts +376 -3
  136. package/lib/esm/sdk.d.ts.map +1 -1
  137. package/lib/esm/sdk.js +1 -0
  138. package/lib/esm/sdk.js.map +1 -1
  139. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts +22 -0
  140. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts.map +1 -0
  141. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.js +501 -0
  142. package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.js.map +1 -0
  143. package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts +3 -1
  144. package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -1
  145. package/lib/esm/tests/api_tests/enduser_fields_sync.test.js +2 -0
  146. package/lib/esm/tests/api_tests/enduser_fields_sync.test.js.map +1 -1
  147. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts +3 -1
  148. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -1
  149. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js +2 -0
  150. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -1
  151. package/lib/esm/tests/api_tests/form_vital_reading.test.d.ts +7 -0
  152. package/lib/esm/tests/api_tests/form_vital_reading.test.d.ts.map +1 -0
  153. package/lib/esm/tests/api_tests/form_vital_reading.test.js +456 -0
  154. package/lib/esm/tests/api_tests/form_vital_reading.test.js.map +1 -0
  155. package/lib/esm/tests/api_tests/journey_statistics_cache.test.d.ts +26 -0
  156. package/lib/esm/tests/api_tests/journey_statistics_cache.test.d.ts.map +1 -0
  157. package/lib/esm/tests/api_tests/journey_statistics_cache.test.js +227 -0
  158. package/lib/esm/tests/api_tests/journey_statistics_cache.test.js.map +1 -0
  159. package/lib/esm/tests/api_tests/journey_step_manual_count.test.d.ts +34 -0
  160. package/lib/esm/tests/api_tests/journey_step_manual_count.test.d.ts.map +1 -0
  161. package/lib/esm/tests/api_tests/journey_step_manual_count.test.js +238 -0
  162. package/lib/esm/tests/api_tests/journey_step_manual_count.test.js.map +1 -0
  163. package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts +3 -1
  164. package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -1
  165. package/lib/esm/tests/api_tests/load_threads_care_team.test.js +2 -0
  166. package/lib/esm/tests/api_tests/load_threads_care_team.test.js.map +1 -1
  167. package/lib/esm/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
  168. package/lib/esm/tests/api_tests/mdi_case_offerings.test.js +139 -9
  169. package/lib/esm/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
  170. package/lib/esm/tests/api_tests/organization_signup.test.d.ts +3 -1
  171. package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -1
  172. package/lib/esm/tests/api_tests/organization_signup.test.js +2 -0
  173. package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -1
  174. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.d.ts +7 -0
  175. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.d.ts.map +1 -0
  176. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.js +566 -0
  177. package/lib/esm/tests/api_tests/phone_embed_leave_message.test.js.map +1 -0
  178. package/lib/esm/tests/api_tests/programs.test.d.ts +3 -1
  179. package/lib/esm/tests/api_tests/programs.test.d.ts.map +1 -1
  180. package/lib/esm/tests/api_tests/programs.test.js +72 -16
  181. package/lib/esm/tests/api_tests/programs.test.js.map +1 -1
  182. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.d.ts +7 -0
  183. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.d.ts.map +1 -0
  184. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.js +728 -0
  185. package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.js.map +1 -0
  186. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +3 -1
  187. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
  188. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +57 -47
  189. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
  190. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +3 -1
  191. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -1
  192. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js +2 -0
  193. package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -1
  194. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.d.ts +3 -1
  195. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.d.ts.map +1 -1
  196. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.js +2 -0
  197. package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.js.map +1 -1
  198. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts +3 -1
  199. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -1
  200. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js +2 -0
  201. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -1
  202. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -1
  203. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +424 -39
  204. package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -1
  205. package/lib/esm/tests/api_tests/security/register_business_id_injection.test.js +2 -2
  206. package/lib/esm/tests/api_tests/security/register_business_id_injection.test.js.map +1 -1
  207. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +3 -1
  208. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -1
  209. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +2 -0
  210. package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -1
  211. package/lib/esm/tests/api_tests/session_for_join_link.test.d.ts +10 -0
  212. package/lib/esm/tests/api_tests/session_for_join_link.test.d.ts.map +1 -0
  213. package/lib/esm/tests/api_tests/session_for_join_link.test.js +142 -0
  214. package/lib/esm/tests/api_tests/session_for_join_link.test.js.map +1 -0
  215. package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -1
  216. package/lib/esm/tests/api_tests/set_fields_order_templates.test.js +191 -88
  217. package/lib/esm/tests/api_tests/set_fields_order_templates.test.js.map +1 -1
  218. package/lib/esm/tests/api_tests/stripe_checkout_items.test.d.ts +17 -0
  219. package/lib/esm/tests/api_tests/stripe_checkout_items.test.d.ts.map +1 -0
  220. package/lib/esm/tests/api_tests/stripe_checkout_items.test.js +214 -0
  221. package/lib/esm/tests/api_tests/stripe_checkout_items.test.js.map +1 -0
  222. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.d.ts +5 -0
  223. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.d.ts.map +1 -0
  224. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.js +388 -0
  225. package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.js.map +1 -0
  226. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +3 -1
  227. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -1
  228. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +2 -0
  229. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -1
  230. package/lib/esm/tests/api_tests/totp_mfa.test.d.ts.map +1 -1
  231. package/lib/esm/tests/api_tests/totp_mfa.test.js +37 -31
  232. package/lib/esm/tests/api_tests/totp_mfa.test.js.map +1 -1
  233. package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts +3 -1
  234. package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -1
  235. package/lib/esm/tests/api_tests/trigger_contains_matching.test.js +2 -0
  236. package/lib/esm/tests/api_tests/trigger_contains_matching.test.js.map +1 -1
  237. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts +3 -1
  238. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -1
  239. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js +2 -0
  240. package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -1
  241. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts +3 -1
  242. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -1
  243. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js +2 -0
  244. package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js.map +1 -1
  245. package/lib/esm/tests/api_tests/vitl_push.test.d.ts +7 -0
  246. package/lib/esm/tests/api_tests/vitl_push.test.d.ts.map +1 -0
  247. package/lib/esm/tests/api_tests/vitl_push.test.js +333 -0
  248. package/lib/esm/tests/api_tests/vitl_push.test.js.map +1 -0
  249. package/lib/esm/tests/setup_isolated.d.ts +48 -0
  250. package/lib/esm/tests/setup_isolated.d.ts.map +1 -1
  251. package/lib/esm/tests/setup_isolated.js +114 -0
  252. package/lib/esm/tests/setup_isolated.js.map +1 -1
  253. package/lib/esm/tests/tests.d.ts.map +1 -1
  254. package/lib/esm/tests/tests.js +379 -283
  255. package/lib/esm/tests/tests.js.map +1 -1
  256. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.d.ts +2 -0
  257. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.d.ts.map +1 -0
  258. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.js +174 -0
  259. package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.js.map +1 -0
  260. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.d.ts +3 -0
  261. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.d.ts.map +1 -0
  262. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.js +117 -0
  263. package/lib/esm/tests/unit_tests/programs_conditional_logic.test.js.map +1 -0
  264. package/lib/esm/tests/webhooks_tests.js +44 -34
  265. package/lib/esm/tests/webhooks_tests.js.map +1 -1
  266. package/lib/tsconfig.tsbuildinfo +1 -1
  267. package/package.json +12 -11
  268. package/src/sdk.ts +9 -2
  269. package/src/tests/api_tests/booking_prevent_upcoming_duplicate.test.ts +541 -0
  270. package/src/tests/api_tests/enduser_fields_sync.test.ts +154 -0
  271. package/src/tests/api_tests/enduser_observations_auto_review.test.ts +4 -0
  272. package/src/tests/api_tests/form_vital_reading.test.ts +393 -0
  273. package/src/tests/api_tests/journey_statistics_cache.test.ts +237 -0
  274. package/src/tests/api_tests/journey_step_manual_count.test.ts +220 -0
  275. package/src/tests/api_tests/load_threads_care_team.test.ts +214 -0
  276. package/src/tests/api_tests/mdi_case_offerings.test.ts +138 -0
  277. package/src/tests/api_tests/organization_signup.test.ts +4 -0
  278. package/src/tests/api_tests/phone_embed_leave_message.test.ts +541 -0
  279. package/src/tests/api_tests/programs.test.ts +351 -0
  280. package/src/tests/api_tests/public_form_case_insensitive_match.test.ts +632 -0
  281. package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +37 -26
  282. package/src/tests/api_tests/security/appointment-reschedule-ownership.test.ts +436 -0
  283. package/src/tests/api_tests/security/file-link-session-confinement.test.ts +4 -0
  284. package/src/tests/api_tests/security/form-logic-js-execution.test.ts +4 -0
  285. package/src/tests/api_tests/security/formsort_webhook_auth_field_lock.test.ts +225 -0
  286. package/src/tests/api_tests/security/register_business_id_injection.test.ts +2 -2
  287. package/src/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.ts +4 -0
  288. package/src/tests/api_tests/session_for_join_link.test.ts +99 -0
  289. package/src/tests/api_tests/set_fields_order_templates.test.ts +94 -9
  290. package/src/tests/api_tests/stripe_checkout_items.test.ts +186 -0
  291. package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +4 -0
  292. package/src/tests/api_tests/totp_mfa.test.ts +10 -5
  293. package/src/tests/api_tests/trigger_contains_matching.test.ts +349 -0
  294. package/src/tests/api_tests/user_logs_restore_deleted.test.ts +419 -0
  295. package/src/tests/api_tests/vital_review_trigger_action.test.ts +455 -0
  296. package/src/tests/api_tests/vitl_push.test.ts +323 -0
  297. package/src/tests/setup_isolated.ts +140 -0
  298. package/src/tests/tests.ts +126 -20
  299. package/src/tests/unit_tests/isolated_suite_discovery.test.ts +178 -0
  300. package/src/tests/unit_tests/programs_conditional_logic.test.ts +189 -0
  301. package/src/tests/unit_tests/trigger_title_partials.test.ts +174 -0
  302. package/src/tests/webhooks_tests.ts +6 -1
  303. package/test_generated.pdf +0 -0
@@ -105,6 +105,19 @@ var host = process.env.API_URL || 'http://localhost:8080';
105
105
  * R14 both grace markers are load-bearing: neither source nor stripeCustomerId alone opens it
106
106
  * R15 the grace is per-request — it does not clear the stored flag
107
107
  *
108
+ * ?alwaysSetPhone=true (CU-86e3k4ptv) is a second per-request override, narrower than ?enduserId=:
109
+ * it allows PHONE ONLY through the lock, on every finalized webhook, overwriting an existing and
110
+ * different number. Email — the password-reset channel this lock exists to protect — stays blocked.
111
+ * G16 (GREEN) the flag overwrites an existing, different phone on a guessed match
112
+ * G17 (GREEN) ...and in that same request the submitted email is still dropped
113
+ * R16 (RED) the identical request WITHOUT the flag leaves both phone and email blocked
114
+ * R17 (RED) the flag does not open email on a phone match either
115
+ * R18 (RED) only the literal string 'true' enables it — ?alwaysSetPhone=1 keeps the lock
116
+ * G18 (GREEN) a flagged webhook still stamps authFieldIntakeDisabled on the response, so later
117
+ * writes through save_enduser_updates_for_form_response stay blocked
118
+ * R19 (RED) it is request-scoped: a flag passed on an earlier webhook for the same
119
+ * responder_uuid does not unlock a later one that omits it
120
+ *
108
121
  * The Stripe grace is deliberately confined to THIS endpoint, which is API-key authenticated. The
109
122
  * anonymous public-form path gets no exemption; that boundary is pinned by G28 in
110
123
  * public_endpoint_auth_field_lock.test.ts.
@@ -192,6 +205,8 @@ export var formsort_webhook_auth_field_lock_tests = function (_a) {
192
205
  url.searchParams.set('enduserId', o.enduserId);
193
206
  if (o.createNewEnduser)
194
207
  url.searchParams.set('createNewEnduser', 'true');
208
+ if (o.alwaysSetPhone !== undefined)
209
+ url.searchParams.set('alwaysSetPhone', o.alwaysSetPhone);
195
210
  return [4 /*yield*/, axios.post(url.toString(), { answers: o.answers, responder_uuid: uuidFor(o.label), finalized: o.finalized }, { validateStatus: function () { return true; } })
196
211
  // In-progress webhooks always return a bare 200 (the handler returns before the returnJSON
197
212
  // block), so `data` is only populated for finalized calls.
@@ -1064,7 +1079,10 @@ export var formsort_webhook_auth_field_lock_tests = function (_a) {
1064
1079
  });
1065
1080
  }); })
1066
1081
  // ---------------------------------------------------------------------------------------------
1067
- // Report
1082
+ // G16 — ?alwaysSetPhone=true: the opt-in allows phone through the lock on a GUESSED match (the
1083
+ // path R2 blocks), overwriting a phone that is already stored and different. Phone is also the
1084
+ // only field this submission changes, which exercises the "at least one update provided" guard —
1085
+ // if the delete happened before that guard, nothing would be written at all.
1068
1086
  // ---------------------------------------------------------------------------------------------
1069
1087
  ];
1070
1088
  case 20:
@@ -1075,6 +1093,373 @@ export var formsort_webhook_auth_field_lock_tests = function (_a) {
1075
1093
  // after the window closes) stays protected.
1076
1094
  // ---------------------------------------------------------------------------------------------
1077
1095
  _h.sent();
1096
+ // ---------------------------------------------------------------------------------------------
1097
+ // G16 — ?alwaysSetPhone=true: the opt-in allows phone through the lock on a GUESSED match (the
1098
+ // path R2 blocks), overwriting a phone that is already stored and different. Phone is also the
1099
+ // only field this submission changes, which exercises the "at least one update provided" guard —
1100
+ // if the delete happened before that guard, nothing would be written at all.
1101
+ // ---------------------------------------------------------------------------------------------
1102
+ return [4 /*yield*/, runScenario('G16', 'alwaysSetPhone=true overwrites an existing phone on a guessed match', function () { return __awaiter(void 0, void 0, void 0, function () {
1103
+ var email, originalPhone, victim, intakePhone, data, enduser;
1104
+ return __generator(this, function (_a) {
1105
+ switch (_a.label) {
1106
+ case 0:
1107
+ email = emailFor('g16');
1108
+ originalPhone = nextPhone();
1109
+ return [4 /*yield*/, makeVictim('g16', { email: email, phone: originalPhone })];
1110
+ case 1:
1111
+ victim = _a.sent();
1112
+ intakePhone = nextPhone();
1113
+ return [4 /*yield*/, postWebhook({
1114
+ label: 'g16',
1115
+ finalized: true,
1116
+ alwaysSetPhone: 'true',
1117
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1118
+ })];
1119
+ case 2:
1120
+ data = (_a.sent()).data;
1121
+ if (!matched('G16', 'alwaysSetPhone=true overwrites an existing phone on a guessed match', data, victim.id))
1122
+ return [2 /*return*/];
1123
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1124
+ case 3:
1125
+ enduser = _a.sent();
1126
+ record('G16', 'alwaysSetPhone=true overwrites an existing phone on a guessed match', (enduser === null || enduser === void 0 ? void 0 : enduser.phone) === intakePhone, "phone=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.phone) || '(unset)', " expected=").concat(intakePhone));
1127
+ return [2 /*return*/];
1128
+ }
1129
+ });
1130
+ }); })
1131
+ // ---------------------------------------------------------------------------------------------
1132
+ // G17 — the same request, with BOTH a new phone and a different email, so the two halves of the
1133
+ // rule are observed together: phone lands, email does not. Shaped like R3 (a draft formed by a
1134
+ // guess, finalized through the sticky flag) because that is the only resolution path where the
1135
+ // submitted email can differ from the stored one while the patient still matches.
1136
+ // ---------------------------------------------------------------------------------------------
1137
+ ];
1138
+ case 21:
1139
+ // ---------------------------------------------------------------------------------------------
1140
+ // G16 — ?alwaysSetPhone=true: the opt-in allows phone through the lock on a GUESSED match (the
1141
+ // path R2 blocks), overwriting a phone that is already stored and different. Phone is also the
1142
+ // only field this submission changes, which exercises the "at least one update provided" guard —
1143
+ // if the delete happened before that guard, nothing would be written at all.
1144
+ // ---------------------------------------------------------------------------------------------
1145
+ _h.sent();
1146
+ // ---------------------------------------------------------------------------------------------
1147
+ // G17 — the same request, with BOTH a new phone and a different email, so the two halves of the
1148
+ // rule are observed together: phone lands, email does not. Shaped like R3 (a draft formed by a
1149
+ // guess, finalized through the sticky flag) because that is the only resolution path where the
1150
+ // submitted email can differ from the stored one while the patient still matches.
1151
+ // ---------------------------------------------------------------------------------------------
1152
+ return [4 /*yield*/, runScenario('G17', 'alwaysSetPhone=true writes phone and still drops email', function () { return __awaiter(void 0, void 0, void 0, function () {
1153
+ var originalEmail, originalPhone, victim, attackerEmail, intakePhone, data, enduser;
1154
+ return __generator(this, function (_a) {
1155
+ switch (_a.label) {
1156
+ case 0:
1157
+ originalEmail = emailFor('g17');
1158
+ originalPhone = nextPhone();
1159
+ return [4 /*yield*/, makeVictim('g17', { email: originalEmail, phone: originalPhone })];
1160
+ case 1:
1161
+ victim = _a.sent();
1162
+ attackerEmail = emailFor('g17-attacker');
1163
+ intakePhone = nextPhone();
1164
+ // draft matches the pre-existing patient by phone -> authFieldIntakeDisabled is stamped on it
1165
+ return [4 /*yield*/, postWebhook({ label: 'g17', finalized: false, answers: [{ key: 'phone', value: originalPhone }] })];
1166
+ case 2:
1167
+ // draft matches the pre-existing patient by phone -> authFieldIntakeDisabled is stamped on it
1168
+ _a.sent();
1169
+ return [4 /*yield*/, postWebhook({
1170
+ label: 'g17',
1171
+ finalized: true,
1172
+ alwaysSetPhone: 'true',
1173
+ answers: [{ key: 'phone', value: intakePhone }, { key: 'email', value: attackerEmail }],
1174
+ })];
1175
+ case 3:
1176
+ data = (_a.sent()).data;
1177
+ if (!matched('G17', 'alwaysSetPhone=true writes phone and still drops email', data, victim.id))
1178
+ return [2 /*return*/];
1179
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1180
+ case 4:
1181
+ enduser = _a.sent();
1182
+ record('G17', 'alwaysSetPhone=true writes phone through the sticky flag', (enduser === null || enduser === void 0 ? void 0 : enduser.phone) === intakePhone, "phone=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.phone) || '(unset)', " expected=").concat(intakePhone));
1183
+ record('G17', 'alwaysSetPhone=true leaves email blocked in the same request', (enduser === null || enduser === void 0 ? void 0 : enduser.email) === originalEmail, "email=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.email) || '(unset)', " expected=").concat(originalEmail));
1184
+ return [2 /*return*/];
1185
+ }
1186
+ });
1187
+ }); })
1188
+ // ---------------------------------------------------------------------------------------------
1189
+ // R16 — the control for G17: byte-identical request without the flag. Both fields stay blocked,
1190
+ // i.e. the default behavior is unchanged.
1191
+ // ---------------------------------------------------------------------------------------------
1192
+ ];
1193
+ case 22:
1194
+ // ---------------------------------------------------------------------------------------------
1195
+ // G17 — the same request, with BOTH a new phone and a different email, so the two halves of the
1196
+ // rule are observed together: phone lands, email does not. Shaped like R3 (a draft formed by a
1197
+ // guess, finalized through the sticky flag) because that is the only resolution path where the
1198
+ // submitted email can differ from the stored one while the patient still matches.
1199
+ // ---------------------------------------------------------------------------------------------
1200
+ _h.sent();
1201
+ // ---------------------------------------------------------------------------------------------
1202
+ // R16 — the control for G17: byte-identical request without the flag. Both fields stay blocked,
1203
+ // i.e. the default behavior is unchanged.
1204
+ // ---------------------------------------------------------------------------------------------
1205
+ return [4 /*yield*/, runScenario('R16', 'without the flag the same request blocks phone and email', function () { return __awaiter(void 0, void 0, void 0, function () {
1206
+ var originalEmail, originalPhone, victim, attackerEmail, intakePhone, data, enduser;
1207
+ return __generator(this, function (_a) {
1208
+ switch (_a.label) {
1209
+ case 0:
1210
+ originalEmail = emailFor('r16');
1211
+ originalPhone = nextPhone();
1212
+ return [4 /*yield*/, makeVictim('r16', { email: originalEmail, phone: originalPhone })];
1213
+ case 1:
1214
+ victim = _a.sent();
1215
+ attackerEmail = emailFor('r16-attacker');
1216
+ intakePhone = nextPhone();
1217
+ return [4 /*yield*/, postWebhook({ label: 'r16', finalized: false, answers: [{ key: 'phone', value: originalPhone }] })];
1218
+ case 2:
1219
+ _a.sent();
1220
+ return [4 /*yield*/, postWebhook({
1221
+ label: 'r16',
1222
+ finalized: true,
1223
+ answers: [{ key: 'phone', value: intakePhone }, { key: 'email', value: attackerEmail }],
1224
+ })];
1225
+ case 3:
1226
+ data = (_a.sent()).data;
1227
+ if (!matched('R16', 'without the flag the same request blocks phone and email', data, victim.id))
1228
+ return [2 /*return*/];
1229
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone || e.email === attackerEmail; })];
1230
+ case 4:
1231
+ enduser = _a.sent();
1232
+ record('R16', 'without the flag phone stays blocked', (enduser === null || enduser === void 0 ? void 0 : enduser.phone) === originalPhone, "phone=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.phone) || '(unset)', " expected=").concat(originalPhone));
1233
+ record('R16', 'without the flag email stays blocked', (enduser === null || enduser === void 0 ? void 0 : enduser.email) === originalEmail, "email=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.email) || '(unset)', " expected=").concat(originalEmail));
1234
+ return [2 /*return*/];
1235
+ }
1236
+ });
1237
+ }); })
1238
+ // ---------------------------------------------------------------------------------------------
1239
+ // R17 — the flag is phone-only in BOTH match directions: matched on phone (R1's shape), the
1240
+ // submitted email must still not land.
1241
+ // ---------------------------------------------------------------------------------------------
1242
+ ];
1243
+ case 23:
1244
+ // ---------------------------------------------------------------------------------------------
1245
+ // R16 — the control for G17: byte-identical request without the flag. Both fields stay blocked,
1246
+ // i.e. the default behavior is unchanged.
1247
+ // ---------------------------------------------------------------------------------------------
1248
+ _h.sent();
1249
+ // ---------------------------------------------------------------------------------------------
1250
+ // R17 — the flag is phone-only in BOTH match directions: matched on phone (R1's shape), the
1251
+ // submitted email must still not land.
1252
+ // ---------------------------------------------------------------------------------------------
1253
+ return [4 /*yield*/, runScenario('R17', 'alwaysSetPhone=true does not repoint email on a phone match', function () { return __awaiter(void 0, void 0, void 0, function () {
1254
+ var originalEmail, phone, victim, attackerEmail, data, enduser;
1255
+ return __generator(this, function (_a) {
1256
+ switch (_a.label) {
1257
+ case 0:
1258
+ originalEmail = emailFor('r17');
1259
+ phone = nextPhone();
1260
+ return [4 /*yield*/, makeVictim('r17', { email: originalEmail, phone: phone })];
1261
+ case 1:
1262
+ victim = _a.sent();
1263
+ attackerEmail = emailFor('r17-attacker');
1264
+ return [4 /*yield*/, postWebhook({
1265
+ label: 'r17',
1266
+ finalized: true,
1267
+ alwaysSetPhone: 'true',
1268
+ answers: [{ key: 'phone', value: phone }, { key: 'email', value: attackerEmail }],
1269
+ })];
1270
+ case 2:
1271
+ data = (_a.sent()).data;
1272
+ if (!matched('R17', 'alwaysSetPhone=true does not repoint email on a phone match', data, victim.id))
1273
+ return [2 /*return*/];
1274
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.email === attackerEmail; })];
1275
+ case 3:
1276
+ enduser = _a.sent();
1277
+ record('R17', 'alwaysSetPhone=true does not repoint email on a phone match', (enduser === null || enduser === void 0 ? void 0 : enduser.email) === originalEmail, "email=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.email) || '(unset)', " expected=").concat(originalEmail));
1278
+ return [2 /*return*/];
1279
+ }
1280
+ });
1281
+ }); })
1282
+ // ---------------------------------------------------------------------------------------------
1283
+ // R18 — parsed as === 'true', matching createNewEnduser / matchByName. Any other value, including
1284
+ // a truthy-looking one, keeps today's behavior.
1285
+ // ---------------------------------------------------------------------------------------------
1286
+ ];
1287
+ case 24:
1288
+ // ---------------------------------------------------------------------------------------------
1289
+ // R17 — the flag is phone-only in BOTH match directions: matched on phone (R1's shape), the
1290
+ // submitted email must still not land.
1291
+ // ---------------------------------------------------------------------------------------------
1292
+ _h.sent();
1293
+ // ---------------------------------------------------------------------------------------------
1294
+ // R18 — parsed as === 'true', matching createNewEnduser / matchByName. Any other value, including
1295
+ // a truthy-looking one, keeps today's behavior.
1296
+ // ---------------------------------------------------------------------------------------------
1297
+ return [4 /*yield*/, runScenario('R18', 'alwaysSetPhone=1 does not enable the opt-in', function () { return __awaiter(void 0, void 0, void 0, function () {
1298
+ var email, originalPhone, victim, intakePhone, data, enduser;
1299
+ return __generator(this, function (_a) {
1300
+ switch (_a.label) {
1301
+ case 0:
1302
+ email = emailFor('r18');
1303
+ originalPhone = nextPhone();
1304
+ return [4 /*yield*/, makeVictim('r18', { email: email, phone: originalPhone })];
1305
+ case 1:
1306
+ victim = _a.sent();
1307
+ intakePhone = nextPhone();
1308
+ return [4 /*yield*/, postWebhook({
1309
+ label: 'r18',
1310
+ finalized: true,
1311
+ alwaysSetPhone: '1',
1312
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1313
+ })];
1314
+ case 2:
1315
+ data = (_a.sent()).data;
1316
+ if (!matched('R18', 'alwaysSetPhone=1 does not enable the opt-in', data, victim.id))
1317
+ return [2 /*return*/];
1318
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1319
+ case 3:
1320
+ enduser = _a.sent();
1321
+ record('R18', 'alwaysSetPhone=1 does not enable the opt-in', (enduser === null || enduser === void 0 ? void 0 : enduser.phone) === originalPhone, "phone=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.phone) || '(unset)', " expected=").concat(originalPhone));
1322
+ return [2 /*return*/];
1323
+ }
1324
+ });
1325
+ }); })
1326
+ // ---------------------------------------------------------------------------------------------
1327
+ // G18 — the opt-in is a per-REQUEST allow, exactly like G11 (?enduserId=) and R15 (the Stripe
1328
+ // grace): the response still records that it attached to a pre-existing patient, so the other
1329
+ // consumer of that flag — save_enduser_updates_for_form_response, reached by later staff edits —
1330
+ // stays protected.
1331
+ // ---------------------------------------------------------------------------------------------
1332
+ ];
1333
+ case 25:
1334
+ // ---------------------------------------------------------------------------------------------
1335
+ // R18 — parsed as === 'true', matching createNewEnduser / matchByName. Any other value, including
1336
+ // a truthy-looking one, keeps today's behavior.
1337
+ // ---------------------------------------------------------------------------------------------
1338
+ _h.sent();
1339
+ // ---------------------------------------------------------------------------------------------
1340
+ // G18 — the opt-in is a per-REQUEST allow, exactly like G11 (?enduserId=) and R15 (the Stripe
1341
+ // grace): the response still records that it attached to a pre-existing patient, so the other
1342
+ // consumer of that flag — save_enduser_updates_for_form_response, reached by later staff edits —
1343
+ // stays protected.
1344
+ // ---------------------------------------------------------------------------------------------
1345
+ return [4 /*yield*/, runScenario('G18', 'alwaysSetPhone does not clear the stored flag', function () { return __awaiter(void 0, void 0, void 0, function () {
1346
+ var email, victim, intakePhone, data, enduser, response;
1347
+ return __generator(this, function (_a) {
1348
+ switch (_a.label) {
1349
+ case 0:
1350
+ email = emailFor('g18');
1351
+ return [4 /*yield*/, makeVictim('g18', { email: email, phone: nextPhone() })];
1352
+ case 1:
1353
+ victim = _a.sent();
1354
+ intakePhone = nextPhone();
1355
+ return [4 /*yield*/, postWebhook({
1356
+ label: 'g18',
1357
+ finalized: true,
1358
+ alwaysSetPhone: 'true',
1359
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1360
+ })];
1361
+ case 2:
1362
+ data = (_a.sent()).data;
1363
+ if (!matched('G18', 'alwaysSetPhone does not clear the stored flag', data, victim.id))
1364
+ return [2 /*return*/];
1365
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1366
+ case 3:
1367
+ enduser = _a.sent();
1368
+ record('G18', 'flagged webhook landed the phone (probe precondition)', (enduser === null || enduser === void 0 ? void 0 : enduser.phone) === intakePhone, "phone=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.phone) || '(unset)', " expected=").concat(intakePhone));
1369
+ if (!data.formResponseId) {
1370
+ skip('G18', 'alwaysSetPhone does not clear the stored flag', 'webhook returned no formResponseId');
1371
+ return [2 /*return*/];
1372
+ }
1373
+ return [4 /*yield*/, sdk.api.form_responses.getOne(data.formResponseId).catch(function () { return null; })];
1374
+ case 4:
1375
+ response = _a.sent();
1376
+ if (!response) {
1377
+ skip('G18', 'alwaysSetPhone does not clear the stored flag', 'form response not readable');
1378
+ }
1379
+ else if (response.authFieldIntakeDisabled === undefined) {
1380
+ // deliberately absent from schema.ts; if reads project it away there is nothing to assert here
1381
+ skip('G18', 'alwaysSetPhone does not clear the stored flag', 'authFieldIntakeDisabled not exposed on read');
1382
+ }
1383
+ else {
1384
+ record('G18', 'alwaysSetPhone does not clear the stored flag', response.authFieldIntakeDisabled === true, "authFieldIntakeDisabled=".concat(response.authFieldIntakeDisabled, " expected=true"));
1385
+ }
1386
+ return [2 /*return*/];
1387
+ }
1388
+ });
1389
+ }); })
1390
+ // ---------------------------------------------------------------------------------------------
1391
+ // R19 — nothing about the opt-in is recorded on the response, so a later webhook for the same
1392
+ // responder_uuid that omits it is blocked as usual. The earlier webhook here must be the DRAFT:
1393
+ // once a response is submitted the handler returns 'Already submitted' before reaching the lock,
1394
+ // which would make a finalized-then-finalized pair pass no matter what the implementation did.
1395
+ // ---------------------------------------------------------------------------------------------
1396
+ ];
1397
+ case 26:
1398
+ // ---------------------------------------------------------------------------------------------
1399
+ // G18 — the opt-in is a per-REQUEST allow, exactly like G11 (?enduserId=) and R15 (the Stripe
1400
+ // grace): the response still records that it attached to a pre-existing patient, so the other
1401
+ // consumer of that flag — save_enduser_updates_for_form_response, reached by later staff edits —
1402
+ // stays protected.
1403
+ // ---------------------------------------------------------------------------------------------
1404
+ _h.sent();
1405
+ // ---------------------------------------------------------------------------------------------
1406
+ // R19 — nothing about the opt-in is recorded on the response, so a later webhook for the same
1407
+ // responder_uuid that omits it is blocked as usual. The earlier webhook here must be the DRAFT:
1408
+ // once a response is submitted the handler returns 'Already submitted' before reaching the lock,
1409
+ // which would make a finalized-then-finalized pair pass no matter what the implementation did.
1410
+ // ---------------------------------------------------------------------------------------------
1411
+ return [4 /*yield*/, runScenario('R19', 'alwaysSetPhone on an earlier webhook does not unlock a later one', function () { return __awaiter(void 0, void 0, void 0, function () {
1412
+ var email, originalPhone, victim, intakePhone, draft, data, enduser;
1413
+ return __generator(this, function (_a) {
1414
+ switch (_a.label) {
1415
+ case 0:
1416
+ email = emailFor('r19');
1417
+ originalPhone = nextPhone();
1418
+ return [4 /*yield*/, makeVictim('r19', { email: email, phone: originalPhone })];
1419
+ case 1:
1420
+ victim = _a.sent();
1421
+ intakePhone = nextPhone();
1422
+ return [4 /*yield*/, postWebhook({
1423
+ label: 'r19',
1424
+ finalized: false,
1425
+ alwaysSetPhone: 'true',
1426
+ answers: [{ key: 'email', value: email }],
1427
+ })];
1428
+ case 2:
1429
+ draft = _a.sent();
1430
+ if (draft.status !== 200) {
1431
+ record('R19', 'alwaysSetPhone on an earlier webhook does not unlock a later one', false, "draft webhook returned ".concat(draft.status));
1432
+ return [2 /*return*/];
1433
+ }
1434
+ return [4 /*yield*/, postWebhook({
1435
+ label: 'r19',
1436
+ finalized: true,
1437
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1438
+ })];
1439
+ case 3:
1440
+ data = (_a.sent()).data;
1441
+ if (!matched('R19', 'alwaysSetPhone on an earlier webhook does not unlock a later one', data, victim.id))
1442
+ return [2 /*return*/];
1443
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1444
+ case 4:
1445
+ enduser = _a.sent();
1446
+ record('R19', 'alwaysSetPhone on an earlier webhook does not unlock a later one', (enduser === null || enduser === void 0 ? void 0 : enduser.phone) === originalPhone, "phone=".concat((enduser === null || enduser === void 0 ? void 0 : enduser.phone) || '(unset)', " expected=").concat(originalPhone));
1447
+ return [2 /*return*/];
1448
+ }
1449
+ });
1450
+ }); })
1451
+ // ---------------------------------------------------------------------------------------------
1452
+ // Report
1453
+ // ---------------------------------------------------------------------------------------------
1454
+ ];
1455
+ case 27:
1456
+ // ---------------------------------------------------------------------------------------------
1457
+ // R19 — nothing about the opt-in is recorded on the response, so a later webhook for the same
1458
+ // responder_uuid that omits it is blocked as usual. The earlier webhook here must be the DRAFT:
1459
+ // once a response is submitted the handler returns 'Already submitted' before reaching the lock,
1460
+ // which would make a finalized-then-finalized pair pass no matter what the implementation did.
1461
+ // ---------------------------------------------------------------------------------------------
1462
+ _h.sent();
1078
1463
  failures = results.filter(function (r) { return !r.ok; });
1079
1464
  skipped = results.filter(function (r) { return !!r.skipped; });
1080
1465
  console.log('');
@@ -1087,69 +1472,69 @@ export var formsort_webhook_auth_field_lock_tests = function (_a) {
1087
1472
  console.log(" \u2514\u2500 ".concat(results.length - failures.length, "/").concat(results.length, " passed, ").concat(skipped.length, " skipped, ").concat(failures.length, " failed"));
1088
1473
  console.log('');
1089
1474
  _b = 0, createdFormIds_1 = createdFormIds;
1090
- _h.label = 21;
1091
- case 21:
1092
- if (!(_b < createdFormIds_1.length)) return [3 /*break*/, 27];
1475
+ _h.label = 28;
1476
+ case 28:
1477
+ if (!(_b < createdFormIds_1.length)) return [3 /*break*/, 34];
1093
1478
  formId = createdFormIds_1[_b];
1094
1479
  return [4 /*yield*/, sdk.api.form_responses.getSome({ filter: { formId: formId } }).catch(function () { return []; })];
1095
- case 22:
1480
+ case 29:
1096
1481
  responses = _h.sent();
1097
1482
  _c = 0, responses_1 = responses;
1098
- _h.label = 23;
1099
- case 23:
1100
- if (!(_c < responses_1.length)) return [3 /*break*/, 26];
1483
+ _h.label = 30;
1484
+ case 30:
1485
+ if (!(_c < responses_1.length)) return [3 /*break*/, 33];
1101
1486
  response = responses_1[_c];
1102
1487
  return [4 /*yield*/, sdk.api.form_responses.deleteOne(response.id).catch(function () { return null; })];
1103
- case 24:
1488
+ case 31:
1104
1489
  _h.sent();
1105
- _h.label = 25;
1106
- case 25:
1490
+ _h.label = 32;
1491
+ case 32:
1107
1492
  _c++;
1108
- return [3 /*break*/, 23];
1109
- case 26:
1493
+ return [3 /*break*/, 30];
1494
+ case 33:
1110
1495
  _b++;
1111
- return [3 /*break*/, 21];
1112
- case 27:
1496
+ return [3 /*break*/, 28];
1497
+ case 34:
1113
1498
  _d = 0, createdFormIds_2 = createdFormIds;
1114
- _h.label = 28;
1115
- case 28:
1116
- if (!(_d < createdFormIds_2.length)) return [3 /*break*/, 31];
1499
+ _h.label = 35;
1500
+ case 35:
1501
+ if (!(_d < createdFormIds_2.length)) return [3 /*break*/, 38];
1117
1502
  id = createdFormIds_2[_d];
1118
1503
  return [4 /*yield*/, sdk.api.forms.deleteOne(id).catch(function () { return null; })];
1119
- case 29:
1504
+ case 36:
1120
1505
  _h.sent();
1121
- _h.label = 30;
1122
- case 30:
1506
+ _h.label = 37;
1507
+ case 37:
1123
1508
  _d++;
1124
- return [3 /*break*/, 28];
1125
- case 31:
1509
+ return [3 /*break*/, 35];
1510
+ case 38:
1126
1511
  uniqueEnduserIds = createdEnduserIds.filter(function (id, i) { return !!id && createdEnduserIds.indexOf(id) === i; });
1127
1512
  _e = 0, uniqueEnduserIds_1 = uniqueEnduserIds;
1128
- _h.label = 32;
1129
- case 32:
1130
- if (!(_e < uniqueEnduserIds_1.length)) return [3 /*break*/, 35];
1513
+ _h.label = 39;
1514
+ case 39:
1515
+ if (!(_e < uniqueEnduserIds_1.length)) return [3 /*break*/, 42];
1131
1516
  id = uniqueEnduserIds_1[_e];
1132
1517
  return [4 /*yield*/, sdk.api.endusers.deleteOne(id).catch(function () { return null; })];
1133
- case 33:
1518
+ case 40:
1134
1519
  _h.sent();
1135
- _h.label = 34;
1136
- case 34:
1520
+ _h.label = 41;
1521
+ case 41:
1137
1522
  _e++;
1138
- return [3 /*break*/, 32];
1139
- case 35:
1523
+ return [3 /*break*/, 39];
1524
+ case 42:
1140
1525
  _f = 0, createdApiKeyIds_1 = createdApiKeyIds;
1141
- _h.label = 36;
1142
- case 36:
1143
- if (!(_f < createdApiKeyIds_1.length)) return [3 /*break*/, 39];
1526
+ _h.label = 43;
1527
+ case 43:
1528
+ if (!(_f < createdApiKeyIds_1.length)) return [3 /*break*/, 46];
1144
1529
  id = createdApiKeyIds_1[_f];
1145
1530
  return [4 /*yield*/, sdk.api.api_keys.deleteOne(id).catch(function () { return null; })];
1146
- case 37:
1531
+ case 44:
1147
1532
  _h.sent();
1148
- _h.label = 38;
1149
- case 38:
1533
+ _h.label = 45;
1534
+ case 45:
1150
1535
  _f++;
1151
- return [3 /*break*/, 36];
1152
- case 39:
1536
+ return [3 /*break*/, 43];
1537
+ case 46:
1153
1538
  // ---------------------------------------------------------------------------------------------
1154
1539
  // Assert — single pass, last, so the full table above is always printed first.
1155
1540
  // ---------------------------------------------------------------------------------------------