@tellescope/sdk 1.250.0 → 1.250.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 (42) hide show
  1. package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.d.ts.map +1 -1
  2. package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js +28 -15
  3. package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
  4. package/lib/cjs/tests/api_tests/medication_added_trigger.test.d.ts.map +1 -1
  5. package/lib/cjs/tests/api_tests/medication_added_trigger.test.js +556 -105
  6. package/lib/cjs/tests/api_tests/medication_added_trigger.test.js.map +1 -1
  7. package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.d.ts +7 -0
  8. package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.d.ts.map +1 -0
  9. package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.js +436 -0
  10. package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.js.map +1 -0
  11. package/lib/cjs/tests/tests.d.ts.map +1 -1
  12. package/lib/cjs/tests/tests.js +149 -141
  13. package/lib/cjs/tests/tests.js.map +1 -1
  14. package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.d.ts +3 -0
  15. package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.d.ts.map +1 -0
  16. package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.js +114 -0
  17. package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.js.map +1 -0
  18. package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.d.ts.map +1 -1
  19. package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js +28 -15
  20. package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
  21. package/lib/esm/tests/api_tests/medication_added_trigger.test.d.ts.map +1 -1
  22. package/lib/esm/tests/api_tests/medication_added_trigger.test.js +556 -105
  23. package/lib/esm/tests/api_tests/medication_added_trigger.test.js.map +1 -1
  24. package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.d.ts +7 -0
  25. package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.d.ts.map +1 -0
  26. package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.js +432 -0
  27. package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.js.map +1 -0
  28. package/lib/esm/tests/tests.d.ts.map +1 -1
  29. package/lib/esm/tests/tests.js +149 -141
  30. package/lib/esm/tests/tests.js.map +1 -1
  31. package/lib/esm/tests/unit_tests/conditional_logic_medication.test.d.ts +3 -0
  32. package/lib/esm/tests/unit_tests/conditional_logic_medication.test.d.ts.map +1 -0
  33. package/lib/esm/tests/unit_tests/conditional_logic_medication.test.js +111 -0
  34. package/lib/esm/tests/unit_tests/conditional_logic_medication.test.js.map +1 -0
  35. package/lib/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +10 -10
  37. package/src/tests/api_tests/enduser_cross_access_isolation.test.ts +26 -0
  38. package/src/tests/api_tests/medication_added_trigger.test.ts +345 -4
  39. package/src/tests/api_tests/outbound_chat_sent_trigger.test.ts +339 -0
  40. package/src/tests/tests.ts +5 -1
  41. package/src/tests/unit_tests/conditional_logic_medication.test.ts +133 -0
  42. package/test_generated.pdf +0 -0
@@ -182,7 +182,7 @@ export const medication_added_trigger_tests = async ({ sdk, sdkNonAdmin } : { sd
182
182
  title: "Medication - Beluga Protocol Test"
183
183
  })
184
184
 
185
- // Simulate the Beluga RX_WRITTEN webhook
185
+ // Simulate the Beluga RX_WRITTEN webhook with two meds covering category present (typical) and "N/A"
186
186
  const webhookResponse = await fetch(`${host}/v1/webhooks/beluga`, {
187
187
  method: 'POST',
188
188
  headers: { 'Content-Type': 'application/json' },
@@ -197,6 +197,15 @@ export const medication_added_trigger_tests = async ({ sdk, sdkNonAdmin } : { sd
197
197
  quantity: '1',
198
198
  medId: 'test-ndc-123',
199
199
  rxId: 'test-rx-456',
200
+ category: 'weightloss1',
201
+ }, {
202
+ name: 'Metformin',
203
+ strength: '500mg',
204
+ refills: '2',
205
+ quantity: '1',
206
+ medId: 'test-ndc-789',
207
+ rxId: 'test-rx-789',
208
+ category: 'N/A',
200
209
  }],
201
210
  }),
202
211
  })
@@ -216,18 +225,83 @@ export const medication_added_trigger_tests = async ({ sdk, sdkNonAdmin } : { sd
216
225
 
217
226
  // Verify the medication was created with correct protocol and source
218
227
  const belugaMeds = await sdk.api.enduser_medications.getSome({ filter: { enduserId: belugaEnduser.id } })
219
- const belugaMed = belugaMeds[0]
228
+ const belugaMedSemaglutide = belugaMeds.find(m => m.title === 'Semaglutide')
229
+ const belugaMedMetformin = belugaMeds.find(m => m.title === 'Metformin')
230
+ if (!belugaMedSemaglutide || !belugaMedMetformin) {
231
+ throw new Error(`Beluga RX_WRITTEN - expected medications missing. Got: ${belugaMeds.map(m => m.title).join(', ')}`)
232
+ }
220
233
  await async_test(
221
234
  "Beluga RX_WRITTEN - Medication has protocol from form belugaVisitType",
222
- async () => belugaMed,
223
- { onResult: (m: EnduserMedication) => m.protocol === 'Weight Loss' && m.source === 'Beluga' && m.title === 'Semaglutide' }
235
+ async () => belugaMedSemaglutide,
236
+ { onResult: (m: EnduserMedication) => m?.protocol === 'Weight Loss' && m?.source === 'Beluga' && m?.title === 'Semaglutide' }
237
+ )
238
+ await async_test(
239
+ "Beluga RX_WRITTEN - Medication has category from webhook",
240
+ async () => belugaMedSemaglutide,
241
+ { onResult: (m: EnduserMedication) => m?.category === 'weightloss1' }
242
+ )
243
+ await async_test(
244
+ "Beluga RX_WRITTEN - Medication preserves N/A category verbatim",
245
+ async () => belugaMedMetformin,
246
+ { onResult: (m: EnduserMedication) => m?.category === 'N/A' }
247
+ )
248
+
249
+ // Backwards-compatibility: a webhook with no category on any med should result in undefined category
250
+ const belugaFormNoCategory = await sdk.api.forms.createOne({
251
+ title: 'Beluga Protocol Test Form (no category)',
252
+ belugaVisitType: 'Weight Loss',
253
+ })
254
+ const belugaEnduserNoCategory = await sdk.api.endusers.createOne({})
255
+ const belugaFormResponseNoCategory = await sdk.api.form_responses.createOne({
256
+ formId: belugaFormNoCategory.id,
257
+ enduserId: belugaEnduserNoCategory.id,
258
+ formTitle: belugaFormNoCategory.title,
259
+ responses: [{
260
+ fieldId: PLACEHOLDER_ID,
261
+ fieldTitle: 'placeholder',
262
+ answer: { type: 'string', value: 'test' },
263
+ }],
264
+ })
265
+
266
+ const webhookResponseNoCategory = await fetch(`${host}/v1/webhooks/beluga`, {
267
+ method: 'POST',
268
+ headers: { 'Content-Type': 'application/json' },
269
+ body: JSON.stringify({
270
+ masterId: `tellescope_${belugaFormResponseNoCategory.id}`,
271
+ event: 'RX_WRITTEN',
272
+ docName: 'Dr. Test',
273
+ medsPrescribed: [{
274
+ name: 'Ibuprofen',
275
+ strength: '200mg',
276
+ refills: '1',
277
+ quantity: '1',
278
+ medId: 'test-ndc-nocat',
279
+ rxId: 'test-rx-nocat',
280
+ }],
281
+ }),
282
+ })
283
+
284
+ if (!webhookResponseNoCategory.ok) {
285
+ throw new Error(`Beluga webhook (no category) failed with status ${webhookResponseNoCategory.status}: ${await webhookResponseNoCategory.text()}`)
286
+ }
287
+
288
+ await wait(undefined, 500)
289
+
290
+ const belugaMedsNoCategory = await sdk.api.enduser_medications.getSome({ filter: { enduserId: belugaEnduserNoCategory.id } })
291
+ const belugaMedNoCategory = belugaMedsNoCategory[0]
292
+ await async_test(
293
+ "Beluga RX_WRITTEN - Medication omits category when not provided",
294
+ async () => belugaMedNoCategory,
295
+ { onResult: (m: EnduserMedication) => m?.category === undefined || m?.category === null }
224
296
  )
225
297
 
226
298
  try {
227
299
  // Clean up Beluga test data
228
300
  await sdk.api.automation_triggers.deleteOne(belugaTrigger.id)
229
301
  await sdk.api.forms.deleteOne(belugaForm.id)
302
+ await sdk.api.forms.deleteOne(belugaFormNoCategory.id)
230
303
  await sdk.api.endusers.deleteOne(belugaEnduser.id) // also cleans up form response and medications
304
+ await sdk.api.endusers.deleteOne(belugaEnduserNoCategory.id) // also cleans up form response and medications
231
305
  } finally {}
232
306
 
233
307
  // ---- Set Fields with {{medication.name}} Test ----
@@ -276,11 +350,278 @@ export const medication_added_trigger_tests = async ({ sdk, sdkNonAdmin } : { sd
276
350
  { onResult: (e: Enduser) => e.fields?.['Specific Med'] === 'Metformin 500mg' }
277
351
  )
278
352
 
353
+ // ---- Set Fields with {{medication.category}} Test ----
354
+ log_header("Medication Added - Set Fields with {{medication.category}}")
355
+
356
+ const setFieldsCategoryTrigger = await sdk.api.automation_triggers.createOne({
357
+ event: { type: 'Medication Added', info: { titles: [], protocols: [] } },
358
+ action: { type: 'Set Fields', info: { fields: [{ name: 'Medication Category', type: 'Custom Value' as const, value: '{{medication.category}}' }] }},
359
+ status: 'Active',
360
+ title: "Medication - Set Fields medication.category"
361
+ })
362
+
363
+ const setFieldsCategoryEnduser = await sdk.api.endusers.createOne({})
364
+
365
+ const setFieldsCategoryMed = await sdk.api.enduser_medications.createOne({
366
+ enduserId: setFieldsCategoryEnduser.id,
367
+ title: 'Ozempic 0.5mg',
368
+ category: 'weightloss1',
369
+ })
370
+ await wait(undefined, 500)
371
+
372
+ await async_test(
373
+ "Medication Added - Set Fields copies medication.category to enduser field",
374
+ () => sdk.api.endusers.getOne(setFieldsCategoryEnduser.id),
375
+ { onResult: (e: Enduser) => e.fields?.['Medication Category'] === 'weightloss1' }
376
+ )
377
+
378
+ // Category absent on medication → placeholder resolves to empty string
379
+ const setFieldsCategoryEnduserEmpty = await sdk.api.endusers.createOne({})
380
+ const setFieldsCategoryMedEmpty = await sdk.api.enduser_medications.createOne({
381
+ enduserId: setFieldsCategoryEnduserEmpty.id,
382
+ title: 'Lisinopril 20mg',
383
+ })
384
+ await wait(undefined, 500)
385
+
386
+ await async_test(
387
+ "Medication Added - Set Fields with no category resolves to empty string",
388
+ () => sdk.api.endusers.getOne(setFieldsCategoryEnduserEmpty.id),
389
+ { onResult: (e: Enduser) => e.fields?.['Medication Category'] === '' }
390
+ )
391
+
279
392
  try {
280
393
  await sdk.api.automation_triggers.deleteOne(setFieldsTrigger.id)
281
394
  await sdk.api.automation_triggers.deleteOne(setFieldsTriggerFiltered.id)
395
+ await sdk.api.automation_triggers.deleteOne(setFieldsCategoryTrigger.id)
282
396
  await sdk.api.endusers.deleteOne(setFieldsEnduser.id)
283
397
  await sdk.api.endusers.deleteOne(setFieldsEnduser2.id)
398
+ await sdk.api.endusers.deleteOne(setFieldsCategoryEnduser.id)
399
+ await sdk.api.endusers.deleteOne(setFieldsCategoryEnduserEmpty.id)
400
+ } finally {}
401
+
402
+ // ---- titleCondition (compound conditional logic on title) ----
403
+ log_header("Medication Added - titleCondition compound logic")
404
+
405
+ // C1: simple $contains — fires for medications whose title contains 'GLP' (case-sensitive)
406
+ const c1Trigger = await sdk.api.automation_triggers.createOne({
407
+ event: {
408
+ type: 'Medication Added',
409
+ info: {
410
+ titles: [],
411
+ protocols: [],
412
+ titleCondition: { condition: { title: { $contains: 'GLP' } } },
413
+ },
414
+ },
415
+ action: { type: 'Add Tags', info: { tags: ['Med-Cond-Contains-GLP'] }},
416
+ status: 'Active',
417
+ title: "Medication - titleCondition contains GLP"
418
+ })
419
+
420
+ const c1MatchEnduser = await sdk.api.endusers.createOne({})
421
+ await sdk.api.enduser_medications.createOne({ enduserId: c1MatchEnduser.id, title: 'Semaglutide GLP-1' })
422
+ await wait(undefined, 500)
423
+ await async_test(
424
+ "titleCondition $contains - matching medication fires",
425
+ () => sdk.api.endusers.getOne(c1MatchEnduser.id),
426
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-Contains-GLP') }
427
+ )
428
+
429
+ const c1MissEnduser = await sdk.api.endusers.createOne({})
430
+ await sdk.api.enduser_medications.createOne({ enduserId: c1MissEnduser.id, title: 'Aspirin' })
431
+ await wait(undefined, 500)
432
+ await async_test(
433
+ "titleCondition $contains - non-matching medication does NOT fire",
434
+ () => sdk.api.endusers.getOne(c1MissEnduser.id),
435
+ { onResult: (e: Enduser) => !e.tags?.includes('Med-Cond-Contains-GLP') }
436
+ )
437
+
438
+ // C2: $ne — fires for everything except 'Placebo'
439
+ const c2Trigger = await sdk.api.automation_triggers.createOne({
440
+ event: {
441
+ type: 'Medication Added',
442
+ info: {
443
+ titles: [],
444
+ protocols: [],
445
+ titleCondition: { condition: { title: { $ne: 'Placebo' } } },
446
+ },
447
+ },
448
+ action: { type: 'Add Tags', info: { tags: ['Med-Cond-Ne-Placebo'] }},
449
+ status: 'Active',
450
+ title: "Medication - titleCondition ne Placebo"
451
+ })
452
+
453
+ const c2MatchEnduser = await sdk.api.endusers.createOne({})
454
+ await sdk.api.enduser_medications.createOne({ enduserId: c2MatchEnduser.id, title: 'Metformin' })
455
+ await wait(undefined, 500)
456
+ await async_test(
457
+ "titleCondition $ne - non-Placebo medication fires",
458
+ () => sdk.api.endusers.getOne(c2MatchEnduser.id),
459
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-Ne-Placebo') }
460
+ )
461
+
462
+ const c2MissEnduser = await sdk.api.endusers.createOne({})
463
+ await sdk.api.enduser_medications.createOne({ enduserId: c2MissEnduser.id, title: 'Placebo' })
464
+ await wait(undefined, 500)
465
+ await async_test(
466
+ "titleCondition $ne - Placebo medication does NOT fire",
467
+ () => sdk.api.endusers.getOne(c2MissEnduser.id),
468
+ { onResult: (e: Enduser) => !e.tags?.includes('Med-Cond-Ne-Placebo') }
469
+ )
470
+
471
+ // C3: compound $and — contains 'mg' AND does not contain 'Placebo' (case-sensitive)
472
+ const c3Trigger = await sdk.api.automation_triggers.createOne({
473
+ event: {
474
+ type: 'Medication Added',
475
+ info: {
476
+ titles: [],
477
+ protocols: [],
478
+ titleCondition: {
479
+ $and: [
480
+ { condition: { title: { $contains: 'mg' } } },
481
+ { condition: { title: { $doesNotContain: 'Placebo' } } },
482
+ ],
483
+ },
484
+ },
485
+ },
486
+ action: { type: 'Add Tags', info: { tags: ['Med-Cond-And'] }},
487
+ status: 'Active',
488
+ title: "Medication - titleCondition compound AND"
489
+ })
490
+
491
+ const c3MatchEnduser = await sdk.api.endusers.createOne({})
492
+ await sdk.api.enduser_medications.createOne({ enduserId: c3MatchEnduser.id, title: 'Lisinopril 10mg' })
493
+ await wait(undefined, 500)
494
+ await async_test(
495
+ "titleCondition $and - both pass fires",
496
+ () => sdk.api.endusers.getOne(c3MatchEnduser.id),
497
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-And') }
498
+ )
499
+
500
+ const c3MissEnduser = await sdk.api.endusers.createOne({})
501
+ await sdk.api.enduser_medications.createOne({ enduserId: c3MissEnduser.id, title: 'Placebo 5mg' })
502
+ await wait(undefined, 500)
503
+ await async_test(
504
+ "titleCondition $and - second branch fails does NOT fire",
505
+ () => sdk.api.endusers.getOne(c3MissEnduser.id),
506
+ { onResult: (e: Enduser) => !e.tags?.includes('Med-Cond-And') }
507
+ )
508
+
509
+ // C4: compound $or — equals 'Aspirin' OR contains 'pril'
510
+ const c4Trigger = await sdk.api.automation_triggers.createOne({
511
+ event: {
512
+ type: 'Medication Added',
513
+ info: {
514
+ titles: [],
515
+ protocols: [],
516
+ titleCondition: {
517
+ $or: [
518
+ { condition: { title: 'Aspirin' } },
519
+ { condition: { title: { $contains: 'pril' } } },
520
+ ],
521
+ },
522
+ },
523
+ },
524
+ action: { type: 'Add Tags', info: { tags: ['Med-Cond-Or'] }},
525
+ status: 'Active',
526
+ title: "Medication - titleCondition compound OR"
527
+ })
528
+
529
+ const c4MatchA = await sdk.api.endusers.createOne({})
530
+ await sdk.api.enduser_medications.createOne({ enduserId: c4MatchA.id, title: 'Aspirin' })
531
+ await wait(undefined, 500)
532
+ await async_test(
533
+ "titleCondition $or - first branch matches fires",
534
+ () => sdk.api.endusers.getOne(c4MatchA.id),
535
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-Or') }
536
+ )
537
+
538
+ const c4MatchB = await sdk.api.endusers.createOne({})
539
+ await sdk.api.enduser_medications.createOne({ enduserId: c4MatchB.id, title: 'Lisinopril' })
540
+ await wait(undefined, 500)
541
+ await async_test(
542
+ "titleCondition $or - second branch matches fires",
543
+ () => sdk.api.endusers.getOne(c4MatchB.id),
544
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-Or') }
545
+ )
546
+
547
+ const c4Miss = await sdk.api.endusers.createOne({})
548
+ await sdk.api.enduser_medications.createOne({ enduserId: c4Miss.id, title: 'Metformin' })
549
+ await wait(undefined, 500)
550
+ await async_test(
551
+ "titleCondition $or - neither branch matches does NOT fire",
552
+ () => sdk.api.endusers.getOne(c4Miss.id),
553
+ { onResult: (e: Enduser) => !e.tags?.includes('Med-Cond-Or') }
554
+ )
555
+
556
+ // C5: backwards compatibility — no titleCondition, titles array still works
557
+ const c5Trigger = await sdk.api.automation_triggers.createOne({
558
+ event: { type: 'Medication Added', info: { titles: ['Atorvastatin'], protocols: [] } },
559
+ action: { type: 'Add Tags', info: { tags: ['Med-Cond-BackCompat'] }},
560
+ status: 'Active',
561
+ title: "Medication - titles array back-compat"
562
+ })
563
+
564
+ const c5Match = await sdk.api.endusers.createOne({})
565
+ await sdk.api.enduser_medications.createOne({ enduserId: c5Match.id, title: 'Atorvastatin' })
566
+ await wait(undefined, 500)
567
+ await async_test(
568
+ "titles back-compat - title match still fires without titleCondition",
569
+ () => sdk.api.endusers.getOne(c5Match.id),
570
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-BackCompat') }
571
+ )
572
+
573
+ const c5Miss = await sdk.api.endusers.createOne({})
574
+ await sdk.api.enduser_medications.createOne({ enduserId: c5Miss.id, title: 'Aspirin' })
575
+ await wait(undefined, 500)
576
+ await async_test(
577
+ "titles back-compat - non-match does NOT fire without titleCondition",
578
+ () => sdk.api.endusers.getOne(c5Miss.id),
579
+ { onResult: (e: Enduser) => !e.tags?.includes('Med-Cond-BackCompat') }
580
+ )
581
+
582
+ // C6: combined — titles array AND titleCondition compose (both must pass)
583
+ const c6Trigger = await sdk.api.automation_triggers.createOne({
584
+ event: {
585
+ type: 'Medication Added',
586
+ info: {
587
+ titles: ['Lisinopril'],
588
+ protocols: [],
589
+ titleCondition: { condition: { title: { $contains: 'Lisin' } } },
590
+ },
591
+ },
592
+ action: { type: 'Add Tags', info: { tags: ['Med-Cond-Combined'] }},
593
+ status: 'Active',
594
+ title: "Medication - titles + titleCondition combined"
595
+ })
596
+
597
+ const c6Match = await sdk.api.endusers.createOne({})
598
+ await sdk.api.enduser_medications.createOne({ enduserId: c6Match.id, title: 'Lisinopril' })
599
+ await wait(undefined, 500)
600
+ await async_test(
601
+ "titleCondition combined - both pass fires",
602
+ () => sdk.api.endusers.getOne(c6Match.id),
603
+ { onResult: (e: Enduser) => !!e.tags?.includes('Med-Cond-Combined') }
604
+ )
605
+
606
+ try {
607
+ await sdk.api.automation_triggers.deleteOne(c1Trigger.id)
608
+ await sdk.api.automation_triggers.deleteOne(c2Trigger.id)
609
+ await sdk.api.automation_triggers.deleteOne(c3Trigger.id)
610
+ await sdk.api.automation_triggers.deleteOne(c4Trigger.id)
611
+ await sdk.api.automation_triggers.deleteOne(c5Trigger.id)
612
+ await sdk.api.automation_triggers.deleteOne(c6Trigger.id)
613
+ await sdk.api.endusers.deleteOne(c1MatchEnduser.id)
614
+ await sdk.api.endusers.deleteOne(c1MissEnduser.id)
615
+ await sdk.api.endusers.deleteOne(c2MatchEnduser.id)
616
+ await sdk.api.endusers.deleteOne(c2MissEnduser.id)
617
+ await sdk.api.endusers.deleteOne(c3MatchEnduser.id)
618
+ await sdk.api.endusers.deleteOne(c3MissEnduser.id)
619
+ await sdk.api.endusers.deleteOne(c4MatchA.id)
620
+ await sdk.api.endusers.deleteOne(c4MatchB.id)
621
+ await sdk.api.endusers.deleteOne(c4Miss.id)
622
+ await sdk.api.endusers.deleteOne(c5Match.id)
623
+ await sdk.api.endusers.deleteOne(c5Miss.id)
624
+ await sdk.api.endusers.deleteOne(c6Match.id)
284
625
  } finally {}
285
626
  }
286
627