@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
@@ -111,6 +111,19 @@ var host = process.env.API_URL || 'http://localhost:8080';
111
111
  * R14 both grace markers are load-bearing: neither source nor stripeCustomerId alone opens it
112
112
  * R15 the grace is per-request — it does not clear the stored flag
113
113
  *
114
+ * ?alwaysSetPhone=true (CU-86e3k4ptv) is a second per-request override, narrower than ?enduserId=:
115
+ * it allows PHONE ONLY through the lock, on every finalized webhook, overwriting an existing and
116
+ * different number. Email — the password-reset channel this lock exists to protect — stays blocked.
117
+ * G16 (GREEN) the flag overwrites an existing, different phone on a guessed match
118
+ * G17 (GREEN) ...and in that same request the submitted email is still dropped
119
+ * R16 (RED) the identical request WITHOUT the flag leaves both phone and email blocked
120
+ * R17 (RED) the flag does not open email on a phone match either
121
+ * R18 (RED) only the literal string 'true' enables it — ?alwaysSetPhone=1 keeps the lock
122
+ * G18 (GREEN) a flagged webhook still stamps authFieldIntakeDisabled on the response, so later
123
+ * writes through save_enduser_updates_for_form_response stay blocked
124
+ * R19 (RED) it is request-scoped: a flag passed on an earlier webhook for the same
125
+ * responder_uuid does not unlock a later one that omits it
126
+ *
114
127
  * The Stripe grace is deliberately confined to THIS endpoint, which is API-key authenticated. The
115
128
  * anonymous public-form path gets no exemption; that boundary is pinned by G28 in
116
129
  * public_endpoint_auth_field_lock.test.ts.
@@ -198,6 +211,8 @@ var formsort_webhook_auth_field_lock_tests = function (_a) {
198
211
  url.searchParams.set('enduserId', o.enduserId);
199
212
  if (o.createNewEnduser)
200
213
  url.searchParams.set('createNewEnduser', 'true');
214
+ if (o.alwaysSetPhone !== undefined)
215
+ url.searchParams.set('alwaysSetPhone', o.alwaysSetPhone);
201
216
  return [4 /*yield*/, axios_1.default.post(url.toString(), { answers: o.answers, responder_uuid: uuidFor(o.label), finalized: o.finalized }, { validateStatus: function () { return true; } })
202
217
  // In-progress webhooks always return a bare 200 (the handler returns before the returnJSON
203
218
  // block), so `data` is only populated for finalized calls.
@@ -1070,7 +1085,10 @@ var formsort_webhook_auth_field_lock_tests = function (_a) {
1070
1085
  });
1071
1086
  }); })
1072
1087
  // ---------------------------------------------------------------------------------------------
1073
- // Report
1088
+ // G16 — ?alwaysSetPhone=true: the opt-in allows phone through the lock on a GUESSED match (the
1089
+ // path R2 blocks), overwriting a phone that is already stored and different. Phone is also the
1090
+ // only field this submission changes, which exercises the "at least one update provided" guard —
1091
+ // if the delete happened before that guard, nothing would be written at all.
1074
1092
  // ---------------------------------------------------------------------------------------------
1075
1093
  ];
1076
1094
  case 20:
@@ -1081,6 +1099,373 @@ var formsort_webhook_auth_field_lock_tests = function (_a) {
1081
1099
  // after the window closes) stays protected.
1082
1100
  // ---------------------------------------------------------------------------------------------
1083
1101
  _h.sent();
1102
+ // ---------------------------------------------------------------------------------------------
1103
+ // G16 — ?alwaysSetPhone=true: the opt-in allows phone through the lock on a GUESSED match (the
1104
+ // path R2 blocks), overwriting a phone that is already stored and different. Phone is also the
1105
+ // only field this submission changes, which exercises the "at least one update provided" guard —
1106
+ // if the delete happened before that guard, nothing would be written at all.
1107
+ // ---------------------------------------------------------------------------------------------
1108
+ 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 () {
1109
+ var email, originalPhone, victim, intakePhone, data, enduser;
1110
+ return __generator(this, function (_a) {
1111
+ switch (_a.label) {
1112
+ case 0:
1113
+ email = emailFor('g16');
1114
+ originalPhone = nextPhone();
1115
+ return [4 /*yield*/, makeVictim('g16', { email: email, phone: originalPhone })];
1116
+ case 1:
1117
+ victim = _a.sent();
1118
+ intakePhone = nextPhone();
1119
+ return [4 /*yield*/, postWebhook({
1120
+ label: 'g16',
1121
+ finalized: true,
1122
+ alwaysSetPhone: 'true',
1123
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1124
+ })];
1125
+ case 2:
1126
+ data = (_a.sent()).data;
1127
+ if (!matched('G16', 'alwaysSetPhone=true overwrites an existing phone on a guessed match', data, victim.id))
1128
+ return [2 /*return*/];
1129
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1130
+ case 3:
1131
+ enduser = _a.sent();
1132
+ 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));
1133
+ return [2 /*return*/];
1134
+ }
1135
+ });
1136
+ }); })
1137
+ // ---------------------------------------------------------------------------------------------
1138
+ // G17 — the same request, with BOTH a new phone and a different email, so the two halves of the
1139
+ // rule are observed together: phone lands, email does not. Shaped like R3 (a draft formed by a
1140
+ // guess, finalized through the sticky flag) because that is the only resolution path where the
1141
+ // submitted email can differ from the stored one while the patient still matches.
1142
+ // ---------------------------------------------------------------------------------------------
1143
+ ];
1144
+ case 21:
1145
+ // ---------------------------------------------------------------------------------------------
1146
+ // G16 — ?alwaysSetPhone=true: the opt-in allows phone through the lock on a GUESSED match (the
1147
+ // path R2 blocks), overwriting a phone that is already stored and different. Phone is also the
1148
+ // only field this submission changes, which exercises the "at least one update provided" guard —
1149
+ // if the delete happened before that guard, nothing would be written at all.
1150
+ // ---------------------------------------------------------------------------------------------
1151
+ _h.sent();
1152
+ // ---------------------------------------------------------------------------------------------
1153
+ // G17 — the same request, with BOTH a new phone and a different email, so the two halves of the
1154
+ // rule are observed together: phone lands, email does not. Shaped like R3 (a draft formed by a
1155
+ // guess, finalized through the sticky flag) because that is the only resolution path where the
1156
+ // submitted email can differ from the stored one while the patient still matches.
1157
+ // ---------------------------------------------------------------------------------------------
1158
+ return [4 /*yield*/, runScenario('G17', 'alwaysSetPhone=true writes phone and still drops email', function () { return __awaiter(void 0, void 0, void 0, function () {
1159
+ var originalEmail, originalPhone, victim, attackerEmail, intakePhone, data, enduser;
1160
+ return __generator(this, function (_a) {
1161
+ switch (_a.label) {
1162
+ case 0:
1163
+ originalEmail = emailFor('g17');
1164
+ originalPhone = nextPhone();
1165
+ return [4 /*yield*/, makeVictim('g17', { email: originalEmail, phone: originalPhone })];
1166
+ case 1:
1167
+ victim = _a.sent();
1168
+ attackerEmail = emailFor('g17-attacker');
1169
+ intakePhone = nextPhone();
1170
+ // draft matches the pre-existing patient by phone -> authFieldIntakeDisabled is stamped on it
1171
+ return [4 /*yield*/, postWebhook({ label: 'g17', finalized: false, answers: [{ key: 'phone', value: originalPhone }] })];
1172
+ case 2:
1173
+ // draft matches the pre-existing patient by phone -> authFieldIntakeDisabled is stamped on it
1174
+ _a.sent();
1175
+ return [4 /*yield*/, postWebhook({
1176
+ label: 'g17',
1177
+ finalized: true,
1178
+ alwaysSetPhone: 'true',
1179
+ answers: [{ key: 'phone', value: intakePhone }, { key: 'email', value: attackerEmail }],
1180
+ })];
1181
+ case 3:
1182
+ data = (_a.sent()).data;
1183
+ if (!matched('G17', 'alwaysSetPhone=true writes phone and still drops email', data, victim.id))
1184
+ return [2 /*return*/];
1185
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1186
+ case 4:
1187
+ enduser = _a.sent();
1188
+ 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));
1189
+ 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));
1190
+ return [2 /*return*/];
1191
+ }
1192
+ });
1193
+ }); })
1194
+ // ---------------------------------------------------------------------------------------------
1195
+ // R16 — the control for G17: byte-identical request without the flag. Both fields stay blocked,
1196
+ // i.e. the default behavior is unchanged.
1197
+ // ---------------------------------------------------------------------------------------------
1198
+ ];
1199
+ case 22:
1200
+ // ---------------------------------------------------------------------------------------------
1201
+ // G17 — the same request, with BOTH a new phone and a different email, so the two halves of the
1202
+ // rule are observed together: phone lands, email does not. Shaped like R3 (a draft formed by a
1203
+ // guess, finalized through the sticky flag) because that is the only resolution path where the
1204
+ // submitted email can differ from the stored one while the patient still matches.
1205
+ // ---------------------------------------------------------------------------------------------
1206
+ _h.sent();
1207
+ // ---------------------------------------------------------------------------------------------
1208
+ // R16 — the control for G17: byte-identical request without the flag. Both fields stay blocked,
1209
+ // i.e. the default behavior is unchanged.
1210
+ // ---------------------------------------------------------------------------------------------
1211
+ 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 () {
1212
+ var originalEmail, originalPhone, victim, attackerEmail, intakePhone, data, enduser;
1213
+ return __generator(this, function (_a) {
1214
+ switch (_a.label) {
1215
+ case 0:
1216
+ originalEmail = emailFor('r16');
1217
+ originalPhone = nextPhone();
1218
+ return [4 /*yield*/, makeVictim('r16', { email: originalEmail, phone: originalPhone })];
1219
+ case 1:
1220
+ victim = _a.sent();
1221
+ attackerEmail = emailFor('r16-attacker');
1222
+ intakePhone = nextPhone();
1223
+ return [4 /*yield*/, postWebhook({ label: 'r16', finalized: false, answers: [{ key: 'phone', value: originalPhone }] })];
1224
+ case 2:
1225
+ _a.sent();
1226
+ return [4 /*yield*/, postWebhook({
1227
+ label: 'r16',
1228
+ finalized: true,
1229
+ answers: [{ key: 'phone', value: intakePhone }, { key: 'email', value: attackerEmail }],
1230
+ })];
1231
+ case 3:
1232
+ data = (_a.sent()).data;
1233
+ if (!matched('R16', 'without the flag the same request blocks phone and email', data, victim.id))
1234
+ return [2 /*return*/];
1235
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone || e.email === attackerEmail; })];
1236
+ case 4:
1237
+ enduser = _a.sent();
1238
+ 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));
1239
+ 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));
1240
+ return [2 /*return*/];
1241
+ }
1242
+ });
1243
+ }); })
1244
+ // ---------------------------------------------------------------------------------------------
1245
+ // R17 — the flag is phone-only in BOTH match directions: matched on phone (R1's shape), the
1246
+ // submitted email must still not land.
1247
+ // ---------------------------------------------------------------------------------------------
1248
+ ];
1249
+ case 23:
1250
+ // ---------------------------------------------------------------------------------------------
1251
+ // R16 — the control for G17: byte-identical request without the flag. Both fields stay blocked,
1252
+ // i.e. the default behavior is unchanged.
1253
+ // ---------------------------------------------------------------------------------------------
1254
+ _h.sent();
1255
+ // ---------------------------------------------------------------------------------------------
1256
+ // R17 — the flag is phone-only in BOTH match directions: matched on phone (R1's shape), the
1257
+ // submitted email must still not land.
1258
+ // ---------------------------------------------------------------------------------------------
1259
+ 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 () {
1260
+ var originalEmail, phone, victim, attackerEmail, data, enduser;
1261
+ return __generator(this, function (_a) {
1262
+ switch (_a.label) {
1263
+ case 0:
1264
+ originalEmail = emailFor('r17');
1265
+ phone = nextPhone();
1266
+ return [4 /*yield*/, makeVictim('r17', { email: originalEmail, phone: phone })];
1267
+ case 1:
1268
+ victim = _a.sent();
1269
+ attackerEmail = emailFor('r17-attacker');
1270
+ return [4 /*yield*/, postWebhook({
1271
+ label: 'r17',
1272
+ finalized: true,
1273
+ alwaysSetPhone: 'true',
1274
+ answers: [{ key: 'phone', value: phone }, { key: 'email', value: attackerEmail }],
1275
+ })];
1276
+ case 2:
1277
+ data = (_a.sent()).data;
1278
+ if (!matched('R17', 'alwaysSetPhone=true does not repoint email on a phone match', data, victim.id))
1279
+ return [2 /*return*/];
1280
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.email === attackerEmail; })];
1281
+ case 3:
1282
+ enduser = _a.sent();
1283
+ 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));
1284
+ return [2 /*return*/];
1285
+ }
1286
+ });
1287
+ }); })
1288
+ // ---------------------------------------------------------------------------------------------
1289
+ // R18 — parsed as === 'true', matching createNewEnduser / matchByName. Any other value, including
1290
+ // a truthy-looking one, keeps today's behavior.
1291
+ // ---------------------------------------------------------------------------------------------
1292
+ ];
1293
+ case 24:
1294
+ // ---------------------------------------------------------------------------------------------
1295
+ // R17 — the flag is phone-only in BOTH match directions: matched on phone (R1's shape), the
1296
+ // submitted email must still not land.
1297
+ // ---------------------------------------------------------------------------------------------
1298
+ _h.sent();
1299
+ // ---------------------------------------------------------------------------------------------
1300
+ // R18 — parsed as === 'true', matching createNewEnduser / matchByName. Any other value, including
1301
+ // a truthy-looking one, keeps today's behavior.
1302
+ // ---------------------------------------------------------------------------------------------
1303
+ return [4 /*yield*/, runScenario('R18', 'alwaysSetPhone=1 does not enable the opt-in', function () { return __awaiter(void 0, void 0, void 0, function () {
1304
+ var email, originalPhone, victim, intakePhone, data, enduser;
1305
+ return __generator(this, function (_a) {
1306
+ switch (_a.label) {
1307
+ case 0:
1308
+ email = emailFor('r18');
1309
+ originalPhone = nextPhone();
1310
+ return [4 /*yield*/, makeVictim('r18', { email: email, phone: originalPhone })];
1311
+ case 1:
1312
+ victim = _a.sent();
1313
+ intakePhone = nextPhone();
1314
+ return [4 /*yield*/, postWebhook({
1315
+ label: 'r18',
1316
+ finalized: true,
1317
+ alwaysSetPhone: '1',
1318
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1319
+ })];
1320
+ case 2:
1321
+ data = (_a.sent()).data;
1322
+ if (!matched('R18', 'alwaysSetPhone=1 does not enable the opt-in', data, victim.id))
1323
+ return [2 /*return*/];
1324
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1325
+ case 3:
1326
+ enduser = _a.sent();
1327
+ 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));
1328
+ return [2 /*return*/];
1329
+ }
1330
+ });
1331
+ }); })
1332
+ // ---------------------------------------------------------------------------------------------
1333
+ // G18 — the opt-in is a per-REQUEST allow, exactly like G11 (?enduserId=) and R15 (the Stripe
1334
+ // grace): the response still records that it attached to a pre-existing patient, so the other
1335
+ // consumer of that flag — save_enduser_updates_for_form_response, reached by later staff edits —
1336
+ // stays protected.
1337
+ // ---------------------------------------------------------------------------------------------
1338
+ ];
1339
+ case 25:
1340
+ // ---------------------------------------------------------------------------------------------
1341
+ // R18 — parsed as === 'true', matching createNewEnduser / matchByName. Any other value, including
1342
+ // a truthy-looking one, keeps today's behavior.
1343
+ // ---------------------------------------------------------------------------------------------
1344
+ _h.sent();
1345
+ // ---------------------------------------------------------------------------------------------
1346
+ // G18 — the opt-in is a per-REQUEST allow, exactly like G11 (?enduserId=) and R15 (the Stripe
1347
+ // grace): the response still records that it attached to a pre-existing patient, so the other
1348
+ // consumer of that flag — save_enduser_updates_for_form_response, reached by later staff edits —
1349
+ // stays protected.
1350
+ // ---------------------------------------------------------------------------------------------
1351
+ return [4 /*yield*/, runScenario('G18', 'alwaysSetPhone does not clear the stored flag', function () { return __awaiter(void 0, void 0, void 0, function () {
1352
+ var email, victim, intakePhone, data, enduser, response;
1353
+ return __generator(this, function (_a) {
1354
+ switch (_a.label) {
1355
+ case 0:
1356
+ email = emailFor('g18');
1357
+ return [4 /*yield*/, makeVictim('g18', { email: email, phone: nextPhone() })];
1358
+ case 1:
1359
+ victim = _a.sent();
1360
+ intakePhone = nextPhone();
1361
+ return [4 /*yield*/, postWebhook({
1362
+ label: 'g18',
1363
+ finalized: true,
1364
+ alwaysSetPhone: 'true',
1365
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1366
+ })];
1367
+ case 2:
1368
+ data = (_a.sent()).data;
1369
+ if (!matched('G18', 'alwaysSetPhone does not clear the stored flag', data, victim.id))
1370
+ return [2 /*return*/];
1371
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1372
+ case 3:
1373
+ enduser = _a.sent();
1374
+ 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));
1375
+ if (!data.formResponseId) {
1376
+ skip('G18', 'alwaysSetPhone does not clear the stored flag', 'webhook returned no formResponseId');
1377
+ return [2 /*return*/];
1378
+ }
1379
+ return [4 /*yield*/, sdk.api.form_responses.getOne(data.formResponseId).catch(function () { return null; })];
1380
+ case 4:
1381
+ response = _a.sent();
1382
+ if (!response) {
1383
+ skip('G18', 'alwaysSetPhone does not clear the stored flag', 'form response not readable');
1384
+ }
1385
+ else if (response.authFieldIntakeDisabled === undefined) {
1386
+ // deliberately absent from schema.ts; if reads project it away there is nothing to assert here
1387
+ skip('G18', 'alwaysSetPhone does not clear the stored flag', 'authFieldIntakeDisabled not exposed on read');
1388
+ }
1389
+ else {
1390
+ record('G18', 'alwaysSetPhone does not clear the stored flag', response.authFieldIntakeDisabled === true, "authFieldIntakeDisabled=".concat(response.authFieldIntakeDisabled, " expected=true"));
1391
+ }
1392
+ return [2 /*return*/];
1393
+ }
1394
+ });
1395
+ }); })
1396
+ // ---------------------------------------------------------------------------------------------
1397
+ // R19 — nothing about the opt-in is recorded on the response, so a later webhook for the same
1398
+ // responder_uuid that omits it is blocked as usual. The earlier webhook here must be the DRAFT:
1399
+ // once a response is submitted the handler returns 'Already submitted' before reaching the lock,
1400
+ // which would make a finalized-then-finalized pair pass no matter what the implementation did.
1401
+ // ---------------------------------------------------------------------------------------------
1402
+ ];
1403
+ case 26:
1404
+ // ---------------------------------------------------------------------------------------------
1405
+ // G18 — the opt-in is a per-REQUEST allow, exactly like G11 (?enduserId=) and R15 (the Stripe
1406
+ // grace): the response still records that it attached to a pre-existing patient, so the other
1407
+ // consumer of that flag — save_enduser_updates_for_form_response, reached by later staff edits —
1408
+ // stays protected.
1409
+ // ---------------------------------------------------------------------------------------------
1410
+ _h.sent();
1411
+ // ---------------------------------------------------------------------------------------------
1412
+ // R19 — nothing about the opt-in is recorded on the response, so a later webhook for the same
1413
+ // responder_uuid that omits it is blocked as usual. The earlier webhook here must be the DRAFT:
1414
+ // once a response is submitted the handler returns 'Already submitted' before reaching the lock,
1415
+ // which would make a finalized-then-finalized pair pass no matter what the implementation did.
1416
+ // ---------------------------------------------------------------------------------------------
1417
+ 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 () {
1418
+ var email, originalPhone, victim, intakePhone, draft, data, enduser;
1419
+ return __generator(this, function (_a) {
1420
+ switch (_a.label) {
1421
+ case 0:
1422
+ email = emailFor('r19');
1423
+ originalPhone = nextPhone();
1424
+ return [4 /*yield*/, makeVictim('r19', { email: email, phone: originalPhone })];
1425
+ case 1:
1426
+ victim = _a.sent();
1427
+ intakePhone = nextPhone();
1428
+ return [4 /*yield*/, postWebhook({
1429
+ label: 'r19',
1430
+ finalized: false,
1431
+ alwaysSetPhone: 'true',
1432
+ answers: [{ key: 'email', value: email }],
1433
+ })];
1434
+ case 2:
1435
+ draft = _a.sent();
1436
+ if (draft.status !== 200) {
1437
+ record('R19', 'alwaysSetPhone on an earlier webhook does not unlock a later one', false, "draft webhook returned ".concat(draft.status));
1438
+ return [2 /*return*/];
1439
+ }
1440
+ return [4 /*yield*/, postWebhook({
1441
+ label: 'r19',
1442
+ finalized: true,
1443
+ answers: [{ key: 'email', value: email }, { key: 'phone', value: intakePhone }],
1444
+ })];
1445
+ case 3:
1446
+ data = (_a.sent()).data;
1447
+ if (!matched('R19', 'alwaysSetPhone on an earlier webhook does not unlock a later one', data, victim.id))
1448
+ return [2 /*return*/];
1449
+ return [4 /*yield*/, pollEnduser(victim.id, function (e) { return e.phone === intakePhone; })];
1450
+ case 4:
1451
+ enduser = _a.sent();
1452
+ 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));
1453
+ return [2 /*return*/];
1454
+ }
1455
+ });
1456
+ }); })
1457
+ // ---------------------------------------------------------------------------------------------
1458
+ // Report
1459
+ // ---------------------------------------------------------------------------------------------
1460
+ ];
1461
+ case 27:
1462
+ // ---------------------------------------------------------------------------------------------
1463
+ // R19 — nothing about the opt-in is recorded on the response, so a later webhook for the same
1464
+ // responder_uuid that omits it is blocked as usual. The earlier webhook here must be the DRAFT:
1465
+ // once a response is submitted the handler returns 'Already submitted' before reaching the lock,
1466
+ // which would make a finalized-then-finalized pair pass no matter what the implementation did.
1467
+ // ---------------------------------------------------------------------------------------------
1468
+ _h.sent();
1084
1469
  failures = results.filter(function (r) { return !r.ok; });
1085
1470
  skipped = results.filter(function (r) { return !!r.skipped; });
1086
1471
  console.log('');
@@ -1093,69 +1478,69 @@ var formsort_webhook_auth_field_lock_tests = function (_a) {
1093
1478
  console.log(" \u2514\u2500 ".concat(results.length - failures.length, "/").concat(results.length, " passed, ").concat(skipped.length, " skipped, ").concat(failures.length, " failed"));
1094
1479
  console.log('');
1095
1480
  _b = 0, createdFormIds_1 = createdFormIds;
1096
- _h.label = 21;
1097
- case 21:
1098
- if (!(_b < createdFormIds_1.length)) return [3 /*break*/, 27];
1481
+ _h.label = 28;
1482
+ case 28:
1483
+ if (!(_b < createdFormIds_1.length)) return [3 /*break*/, 34];
1099
1484
  formId = createdFormIds_1[_b];
1100
1485
  return [4 /*yield*/, sdk.api.form_responses.getSome({ filter: { formId: formId } }).catch(function () { return []; })];
1101
- case 22:
1486
+ case 29:
1102
1487
  responses = _h.sent();
1103
1488
  _c = 0, responses_1 = responses;
1104
- _h.label = 23;
1105
- case 23:
1106
- if (!(_c < responses_1.length)) return [3 /*break*/, 26];
1489
+ _h.label = 30;
1490
+ case 30:
1491
+ if (!(_c < responses_1.length)) return [3 /*break*/, 33];
1107
1492
  response = responses_1[_c];
1108
1493
  return [4 /*yield*/, sdk.api.form_responses.deleteOne(response.id).catch(function () { return null; })];
1109
- case 24:
1494
+ case 31:
1110
1495
  _h.sent();
1111
- _h.label = 25;
1112
- case 25:
1496
+ _h.label = 32;
1497
+ case 32:
1113
1498
  _c++;
1114
- return [3 /*break*/, 23];
1115
- case 26:
1499
+ return [3 /*break*/, 30];
1500
+ case 33:
1116
1501
  _b++;
1117
- return [3 /*break*/, 21];
1118
- case 27:
1502
+ return [3 /*break*/, 28];
1503
+ case 34:
1119
1504
  _d = 0, createdFormIds_2 = createdFormIds;
1120
- _h.label = 28;
1121
- case 28:
1122
- if (!(_d < createdFormIds_2.length)) return [3 /*break*/, 31];
1505
+ _h.label = 35;
1506
+ case 35:
1507
+ if (!(_d < createdFormIds_2.length)) return [3 /*break*/, 38];
1123
1508
  id = createdFormIds_2[_d];
1124
1509
  return [4 /*yield*/, sdk.api.forms.deleteOne(id).catch(function () { return null; })];
1125
- case 29:
1510
+ case 36:
1126
1511
  _h.sent();
1127
- _h.label = 30;
1128
- case 30:
1512
+ _h.label = 37;
1513
+ case 37:
1129
1514
  _d++;
1130
- return [3 /*break*/, 28];
1131
- case 31:
1515
+ return [3 /*break*/, 35];
1516
+ case 38:
1132
1517
  uniqueEnduserIds = createdEnduserIds.filter(function (id, i) { return !!id && createdEnduserIds.indexOf(id) === i; });
1133
1518
  _e = 0, uniqueEnduserIds_1 = uniqueEnduserIds;
1134
- _h.label = 32;
1135
- case 32:
1136
- if (!(_e < uniqueEnduserIds_1.length)) return [3 /*break*/, 35];
1519
+ _h.label = 39;
1520
+ case 39:
1521
+ if (!(_e < uniqueEnduserIds_1.length)) return [3 /*break*/, 42];
1137
1522
  id = uniqueEnduserIds_1[_e];
1138
1523
  return [4 /*yield*/, sdk.api.endusers.deleteOne(id).catch(function () { return null; })];
1139
- case 33:
1524
+ case 40:
1140
1525
  _h.sent();
1141
- _h.label = 34;
1142
- case 34:
1526
+ _h.label = 41;
1527
+ case 41:
1143
1528
  _e++;
1144
- return [3 /*break*/, 32];
1145
- case 35:
1529
+ return [3 /*break*/, 39];
1530
+ case 42:
1146
1531
  _f = 0, createdApiKeyIds_1 = createdApiKeyIds;
1147
- _h.label = 36;
1148
- case 36:
1149
- if (!(_f < createdApiKeyIds_1.length)) return [3 /*break*/, 39];
1532
+ _h.label = 43;
1533
+ case 43:
1534
+ if (!(_f < createdApiKeyIds_1.length)) return [3 /*break*/, 46];
1150
1535
  id = createdApiKeyIds_1[_f];
1151
1536
  return [4 /*yield*/, sdk.api.api_keys.deleteOne(id).catch(function () { return null; })];
1152
- case 37:
1537
+ case 44:
1153
1538
  _h.sent();
1154
- _h.label = 38;
1155
- case 38:
1539
+ _h.label = 45;
1540
+ case 45:
1156
1541
  _f++;
1157
- return [3 /*break*/, 36];
1158
- case 39:
1542
+ return [3 /*break*/, 43];
1543
+ case 46:
1159
1544
  // ---------------------------------------------------------------------------------------------
1160
1545
  // Assert — single pass, last, so the full table above is always printed first.
1161
1546
  // ---------------------------------------------------------------------------------------------