@hipnation-truth/sdk 0.1.0

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.
package/dist/index.js ADDED
@@ -0,0 +1,927 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __pow = Math.pow;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __export = (target, all) => {
22
+ for (var name in all)
23
+ __defProp(target, name, { get: all[name], enumerable: true });
24
+ };
25
+ var __copyProps = (to, from, except, desc) => {
26
+ if (from && typeof from === "object" || typeof from === "function") {
27
+ for (let key of __getOwnPropNames(from))
28
+ if (!__hasOwnProp.call(to, key) && key !== except)
29
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
30
+ }
31
+ return to;
32
+ };
33
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
+ var __async = (__this, __arguments, generator) => {
35
+ return new Promise((resolve, reject) => {
36
+ var fulfilled = (value) => {
37
+ try {
38
+ step(generator.next(value));
39
+ } catch (e) {
40
+ reject(e);
41
+ }
42
+ };
43
+ var rejected = (value) => {
44
+ try {
45
+ step(generator.throw(value));
46
+ } catch (e) {
47
+ reject(e);
48
+ }
49
+ };
50
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
51
+ step((generator = generator.apply(__this, __arguments)).next());
52
+ });
53
+ };
54
+
55
+ // src/index.ts
56
+ var src_exports = {};
57
+ __export(src_exports, {
58
+ AUTH_EVENTS: () => AUTH_EVENTS,
59
+ AppointmentResource: () => AppointmentResource,
60
+ CALL_EVENTS: () => CALL_EVENTS,
61
+ CONVERSATION_EVENTS: () => CONVERSATION_EVENTS,
62
+ DialpadProxyError: () => DialpadProxyError,
63
+ DialpadResource: () => DialpadResource,
64
+ ENVIRONMENTS: () => ENVIRONMENTS,
65
+ EVENT_TYPES: () => EVENT_TYPES,
66
+ EhrProviderProxy: () => EhrProviderProxy,
67
+ EhrProxyError: () => EhrProxyError,
68
+ EhrResource: () => EhrResource,
69
+ MessagesResource: () => MessagesResource,
70
+ NOTIFICATION_EVENTS: () => NOTIFICATION_EVENTS,
71
+ PROVIDER_EVENTS: () => PROVIDER_EVENTS,
72
+ PatientResource: () => PatientResource,
73
+ REMINDER_EVENTS: () => REMINDER_EVENTS,
74
+ SECURITY_EVENTS: () => SECURITY_EVENTS,
75
+ TASK_EVENTS: () => TASK_EVENTS,
76
+ TRANSLATION_EVENTS: () => TRANSLATION_EVENTS,
77
+ Tracker: () => Tracker,
78
+ TruthClient: () => TruthClient,
79
+ generateUuidV7: () => generateUuidV7
80
+ });
81
+ module.exports = __toCommonJS(src_exports);
82
+
83
+ // src/client.ts
84
+ var import_browser = require("convex/browser");
85
+
86
+ // src/resources/appointments.ts
87
+ var AppointmentResource = class {
88
+ constructor(convexClient) {
89
+ this.convex = convexClient;
90
+ }
91
+ /**
92
+ * Get an appointment by its Truth platform ID.
93
+ */
94
+ get(id) {
95
+ return __async(this, null, function* () {
96
+ try {
97
+ const result = yield this.convex.query(
98
+ "appointments:getById",
99
+ { id }
100
+ );
101
+ return result != null ? result : null;
102
+ } catch (e) {
103
+ return null;
104
+ }
105
+ });
106
+ }
107
+ /**
108
+ * List appointments with optional filters, pagination, and limit.
109
+ */
110
+ list(options) {
111
+ return __async(this, null, function* () {
112
+ try {
113
+ const result = yield this.convex.query(
114
+ "appointments:list",
115
+ {
116
+ patientId: options == null ? void 0 : options.patientId,
117
+ startDate: options == null ? void 0 : options.startDate,
118
+ endDate: options == null ? void 0 : options.endDate,
119
+ status: options == null ? void 0 : options.status,
120
+ limit: options == null ? void 0 : options.limit,
121
+ cursor: options == null ? void 0 : options.cursor
122
+ }
123
+ );
124
+ const typed = result;
125
+ return typed != null ? typed : { data: [], cursor: null, hasMore: false };
126
+ } catch (e) {
127
+ return { data: [], cursor: null, hasMore: false };
128
+ }
129
+ });
130
+ }
131
+ };
132
+
133
+ // src/resources/dialpad.ts
134
+ var DialpadResource = class {
135
+ constructor(apiBaseUrl) {
136
+ this.baseUrl = apiBaseUrl;
137
+ }
138
+ /**
139
+ * Send an SMS or MMS message via Dialpad.
140
+ */
141
+ sendSms(params) {
142
+ return __async(this, null, function* () {
143
+ const body = __spreadValues(__spreadValues({
144
+ to_numbers: [params.to_number],
145
+ from_number: params.from_number,
146
+ infer_country_code: false
147
+ }, params.message ? { text: params.message } : {}), params.media ? { media: params.media } : {});
148
+ return this.post("/sms", body);
149
+ });
150
+ }
151
+ /**
152
+ * Initiate an outbound call from a Dialpad user to a phone number.
153
+ */
154
+ initiateCall(userId, phoneNumber) {
155
+ return __async(this, null, function* () {
156
+ return this.post(`/users/${userId}/initiate_call`, {
157
+ phone_number: phoneNumber
158
+ });
159
+ });
160
+ }
161
+ /**
162
+ * Hang up an active call.
163
+ */
164
+ hangupCall(callId) {
165
+ return __async(this, null, function* () {
166
+ yield this.put(`/call/${callId}/actions/hangup`);
167
+ });
168
+ }
169
+ /**
170
+ * Get the status of a call.
171
+ */
172
+ getCallStatus(callId) {
173
+ return __async(this, null, function* () {
174
+ try {
175
+ return yield this.get(`/call/${callId}`);
176
+ } catch (error) {
177
+ if (error instanceof DialpadProxyError && error.status === 404) {
178
+ return null;
179
+ }
180
+ throw error;
181
+ }
182
+ });
183
+ }
184
+ /**
185
+ * Get a Dialpad user by their user ID.
186
+ */
187
+ getUser(userId) {
188
+ return __async(this, null, function* () {
189
+ try {
190
+ return yield this.get(`/users/${userId}`);
191
+ } catch (error) {
192
+ if (error instanceof DialpadProxyError && error.status === 404) {
193
+ return null;
194
+ }
195
+ throw error;
196
+ }
197
+ });
198
+ }
199
+ /**
200
+ * Find a Dialpad user by email.
201
+ */
202
+ getUserByEmail(email) {
203
+ return __async(this, null, function* () {
204
+ var _a, _b;
205
+ const result = yield this.get("/users", {
206
+ email
207
+ });
208
+ return (_b = (_a = result.items) == null ? void 0 : _a[0]) != null ? _b : null;
209
+ });
210
+ }
211
+ /**
212
+ * Find a Dialpad user by phone number.
213
+ */
214
+ getUserByPhoneNumber(phoneNumber) {
215
+ return __async(this, null, function* () {
216
+ var _a, _b;
217
+ const result = yield this.get("/users", {
218
+ number: phoneNumber
219
+ });
220
+ return (_b = (_a = result.items) == null ? void 0 : _a[0]) != null ? _b : null;
221
+ });
222
+ }
223
+ /**
224
+ * Get information about a Dialpad phone number.
225
+ */
226
+ getNumberInfo(phoneNumber) {
227
+ return __async(this, null, function* () {
228
+ try {
229
+ const cleanNumber = phoneNumber.replace(/[^\d+]/g, "");
230
+ return yield this.get(
231
+ `/numbers/${encodeURIComponent(cleanNumber)}`
232
+ );
233
+ } catch (error) {
234
+ if (error instanceof DialpadProxyError && error.status === 404) {
235
+ return null;
236
+ }
237
+ throw error;
238
+ }
239
+ });
240
+ }
241
+ /**
242
+ * Authenticate a voicemail download URL. Truth appends the Dialpad API key,
243
+ * follows redirects, and returns the clean URL for client-side playback.
244
+ */
245
+ authenticateVoicemail(voicemailLink) {
246
+ return __async(this, null, function* () {
247
+ const url = `${this.baseUrl}/api/messages/dialpad/voicemail/authenticate`;
248
+ const response = yield fetch(url, {
249
+ method: "POST",
250
+ headers: { "Content-Type": "application/json" },
251
+ body: JSON.stringify({ voicemail_link: voicemailLink })
252
+ });
253
+ const result = yield response.json();
254
+ if (!response.ok || !result.success) {
255
+ return null;
256
+ }
257
+ return result.authenticated_url;
258
+ });
259
+ }
260
+ // -----------------------------------------------------------------------
261
+ // Internal HTTP helpers
262
+ // -----------------------------------------------------------------------
263
+ get(path, params) {
264
+ return __async(this, null, function* () {
265
+ const url = new URL(`/api/messages/dialpad${path}`, this.baseUrl);
266
+ if (params) {
267
+ for (const [key, value] of Object.entries(params)) {
268
+ if (value !== void 0) {
269
+ url.searchParams.set(key, String(value));
270
+ }
271
+ }
272
+ }
273
+ const response = yield fetch(url.toString(), {
274
+ method: "GET",
275
+ headers: { Accept: "application/json" }
276
+ });
277
+ if (!response.ok) {
278
+ throw new DialpadProxyError("GET", path, response.status);
279
+ }
280
+ return yield response.json();
281
+ });
282
+ }
283
+ post(path, body) {
284
+ return __async(this, null, function* () {
285
+ const url = `${this.baseUrl}/api/messages/dialpad${path}`;
286
+ const response = yield fetch(url, {
287
+ method: "POST",
288
+ headers: {
289
+ "Content-Type": "application/json",
290
+ Accept: "application/json"
291
+ },
292
+ body: body !== void 0 ? JSON.stringify(body) : void 0
293
+ });
294
+ if (!response.ok) {
295
+ throw new DialpadProxyError("POST", path, response.status);
296
+ }
297
+ return yield response.json();
298
+ });
299
+ }
300
+ put(path, body) {
301
+ return __async(this, null, function* () {
302
+ var _a;
303
+ const url = `${this.baseUrl}/api/messages/dialpad${path}`;
304
+ const response = yield fetch(url, {
305
+ method: "PUT",
306
+ headers: {
307
+ "Content-Type": "application/json",
308
+ Accept: "application/json"
309
+ },
310
+ body: body !== void 0 ? JSON.stringify(body) : void 0
311
+ });
312
+ if (!response.ok) {
313
+ throw new DialpadProxyError("PUT", path, response.status);
314
+ }
315
+ if ((_a = response.headers.get("content-type")) == null ? void 0 : _a.includes("json")) {
316
+ return yield response.json();
317
+ }
318
+ return void 0;
319
+ });
320
+ }
321
+ };
322
+ var MessagesResource = class {
323
+ constructor(apiBaseUrl) {
324
+ this.dialpad = new DialpadResource(apiBaseUrl);
325
+ }
326
+ };
327
+ var DialpadProxyError = class extends Error {
328
+ constructor(method, path, status) {
329
+ super(
330
+ `Dialpad proxy error: ${method} /api/messages/dialpad${path} returned ${status}`
331
+ );
332
+ this.name = "DialpadProxyError";
333
+ this.method = method;
334
+ this.path = path;
335
+ this.status = status;
336
+ }
337
+ };
338
+
339
+ // src/resources/ehr.ts
340
+ var EhrProviderProxy = class {
341
+ constructor(apiBaseUrl, provider) {
342
+ this.baseUrl = apiBaseUrl;
343
+ this.provider = provider;
344
+ }
345
+ /**
346
+ * GET request to the EHR proxy.
347
+ * @param path — path relative to the provider root (e.g., "/patients/123/")
348
+ * @param params — optional query parameters
349
+ */
350
+ get(path, params) {
351
+ return __async(this, null, function* () {
352
+ const url = new URL(`/api/ehr/${this.provider}${path}`, this.baseUrl);
353
+ if (params) {
354
+ for (const [key, value] of Object.entries(params)) {
355
+ if (value !== void 0) {
356
+ url.searchParams.set(key, String(value));
357
+ }
358
+ }
359
+ }
360
+ const response = yield fetch(url.toString(), {
361
+ method: "GET",
362
+ headers: { Accept: "application/json" }
363
+ });
364
+ if (!response.ok) {
365
+ throw new EhrProxyError(this.provider, "GET", path, response.status);
366
+ }
367
+ return yield response.json();
368
+ });
369
+ }
370
+ /**
371
+ * POST request to the EHR proxy.
372
+ */
373
+ post(path, body) {
374
+ return __async(this, null, function* () {
375
+ const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;
376
+ const response = yield fetch(url, {
377
+ method: "POST",
378
+ headers: {
379
+ "Content-Type": "application/json",
380
+ Accept: "application/json"
381
+ },
382
+ body: body !== void 0 ? JSON.stringify(body) : void 0
383
+ });
384
+ if (!response.ok) {
385
+ throw new EhrProxyError(this.provider, "POST", path, response.status);
386
+ }
387
+ return yield response.json();
388
+ });
389
+ }
390
+ /**
391
+ * PUT request to the EHR proxy.
392
+ */
393
+ put(path, body) {
394
+ return __async(this, null, function* () {
395
+ const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;
396
+ const response = yield fetch(url, {
397
+ method: "PUT",
398
+ headers: {
399
+ "Content-Type": "application/json",
400
+ Accept: "application/json"
401
+ },
402
+ body: body !== void 0 ? JSON.stringify(body) : void 0
403
+ });
404
+ if (!response.ok) {
405
+ throw new EhrProxyError(this.provider, "PUT", path, response.status);
406
+ }
407
+ return yield response.json();
408
+ });
409
+ }
410
+ /**
411
+ * PATCH request to the EHR proxy.
412
+ */
413
+ patch(path, body) {
414
+ return __async(this, null, function* () {
415
+ const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;
416
+ const response = yield fetch(url, {
417
+ method: "PATCH",
418
+ headers: {
419
+ "Content-Type": "application/json",
420
+ Accept: "application/json"
421
+ },
422
+ body: body !== void 0 ? JSON.stringify(body) : void 0
423
+ });
424
+ if (!response.ok) {
425
+ throw new EhrProxyError(this.provider, "PATCH", path, response.status);
426
+ }
427
+ return yield response.json();
428
+ });
429
+ }
430
+ /**
431
+ * DELETE request to the EHR proxy.
432
+ */
433
+ delete(path) {
434
+ return __async(this, null, function* () {
435
+ const url = `${this.baseUrl}/api/ehr/${this.provider}${path}`;
436
+ const response = yield fetch(url, {
437
+ method: "DELETE",
438
+ headers: { Accept: "application/json" }
439
+ });
440
+ if (!response.ok) {
441
+ throw new EhrProxyError(this.provider, "DELETE", path, response.status);
442
+ }
443
+ return yield response.json();
444
+ });
445
+ }
446
+ };
447
+ var EhrResource = class {
448
+ constructor(apiBaseUrl) {
449
+ this.elation = new EhrProviderProxy(apiBaseUrl, "elation");
450
+ this.hint = new EhrProviderProxy(apiBaseUrl, "hint");
451
+ }
452
+ };
453
+ var EhrProxyError = class extends Error {
454
+ constructor(provider, method, path, status) {
455
+ super(
456
+ `EHR proxy error: ${method} /api/ehr/${provider}${path} returned ${status}`
457
+ );
458
+ this.name = "EhrProxyError";
459
+ this.provider = provider;
460
+ this.method = method;
461
+ this.path = path;
462
+ this.status = status;
463
+ }
464
+ };
465
+
466
+ // src/resources/patients.ts
467
+ var PatientResource = class {
468
+ constructor(convexClient) {
469
+ this.convex = convexClient;
470
+ }
471
+ /**
472
+ * Get a patient by their Truth platform ID.
473
+ */
474
+ get(id) {
475
+ return __async(this, null, function* () {
476
+ try {
477
+ const result = yield this.convex.query(
478
+ "patients:getById",
479
+ {
480
+ id
481
+ }
482
+ );
483
+ return result != null ? result : null;
484
+ } catch (e) {
485
+ return null;
486
+ }
487
+ });
488
+ }
489
+ /**
490
+ * Get a patient by their Elation EHR ID.
491
+ */
492
+ getByElationId(elationId) {
493
+ return __async(this, null, function* () {
494
+ try {
495
+ const result = yield this.convex.query(
496
+ "patients:getByElationId",
497
+ { elationId }
498
+ );
499
+ return result != null ? result : null;
500
+ } catch (e) {
501
+ return null;
502
+ }
503
+ });
504
+ }
505
+ /**
506
+ * Get a patient by their Hint EHR ID.
507
+ */
508
+ getByHintId(hintId) {
509
+ return __async(this, null, function* () {
510
+ try {
511
+ const result = yield this.convex.query(
512
+ "patients:getByHintId",
513
+ {
514
+ hintId
515
+ }
516
+ );
517
+ return result != null ? result : null;
518
+ } catch (e) {
519
+ return null;
520
+ }
521
+ });
522
+ }
523
+ /**
524
+ * List patients with optional search, pagination, and limit.
525
+ */
526
+ list(options) {
527
+ return __async(this, null, function* () {
528
+ try {
529
+ const result = yield this.convex.query(
530
+ "patients:list",
531
+ {
532
+ search: options == null ? void 0 : options.search,
533
+ limit: options == null ? void 0 : options.limit,
534
+ cursor: options == null ? void 0 : options.cursor
535
+ }
536
+ );
537
+ const typed = result;
538
+ return typed != null ? typed : { data: [], cursor: null, hasMore: false };
539
+ } catch (e) {
540
+ return { data: [], cursor: null, hasMore: false };
541
+ }
542
+ });
543
+ }
544
+ };
545
+
546
+ // src/tracking/tracker.ts
547
+ function generateUuidV7() {
548
+ const now = Date.now();
549
+ const timeBytes = new Uint8Array(6);
550
+ let ts = now;
551
+ for (let i = 5; i >= 0; i--) {
552
+ timeBytes[i] = ts & 255;
553
+ ts = Math.floor(ts / 256);
554
+ }
555
+ const randomBytes = new Uint8Array(10);
556
+ if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.getRandomValues) {
557
+ globalThis.crypto.getRandomValues(randomBytes);
558
+ } else {
559
+ for (let i = 0; i < 10; i++) {
560
+ randomBytes[i] = Math.floor(Math.random() * 256);
561
+ }
562
+ }
563
+ const bytes = new Uint8Array(16);
564
+ bytes.set(timeBytes, 0);
565
+ bytes.set(randomBytes, 6);
566
+ bytes[6] = bytes[6] & 15 | 112;
567
+ bytes[8] = bytes[8] & 63 | 128;
568
+ const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
569
+ return [
570
+ hex.slice(0, 8),
571
+ hex.slice(8, 12),
572
+ hex.slice(12, 16),
573
+ hex.slice(16, 20),
574
+ hex.slice(20, 32)
575
+ ].join("-");
576
+ }
577
+ var API_URLS = {
578
+ local: "http://localhost:3000",
579
+ staging: "https://app.sandbox.communication-hub.com",
580
+ sandbox: "https://app.sandbox.communication-hub.com",
581
+ uat: "https://app.sandbox.communication-hub.com",
582
+ production: "https://app.truth.communication-hub.com"
583
+ };
584
+ var DEFAULT_BATCH_SIZE = 25;
585
+ var DEFAULT_FLUSH_INTERVAL_MS = 5e3;
586
+ var MAX_RETRIES = 3;
587
+ var BASE_RETRY_DELAY_MS = 500;
588
+ var Tracker = class {
589
+ constructor(config) {
590
+ this.queue = [];
591
+ this.flushTimer = null;
592
+ this.isFlushing = false;
593
+ this.isShutdown = false;
594
+ var _a, _b;
595
+ this.config = config;
596
+ this.apiUrl = (_b = (_a = config.apiBaseUrl) != null ? _a : API_URLS[config.environment]) != null ? _b : API_URLS.local;
597
+ this.startFlushInterval();
598
+ this.registerShutdownHooks();
599
+ }
600
+ /**
601
+ * Set the default actor context for subsequent events.
602
+ */
603
+ setActor(actor) {
604
+ this.defaultActor = actor;
605
+ }
606
+ /**
607
+ * Enqueue a typed event for delivery. This is fire-and-forget from
608
+ * the caller's perspective -- events are buffered and flushed in batches.
609
+ */
610
+ track(eventType, payload, options) {
611
+ var _a, _b, _c;
612
+ if (this.isShutdown) {
613
+ return;
614
+ }
615
+ const now = (/* @__PURE__ */ new Date()).toISOString();
616
+ const envelope = {
617
+ event_id: generateUuidV7(),
618
+ event_type: eventType,
619
+ schema_version: 1,
620
+ occurred_at: (_a = options == null ? void 0 : options.occurredAt) != null ? _a : now,
621
+ received_at: now,
622
+ source: this.config.source,
623
+ source_version: this.config.sourceVersion,
624
+ tenant_id: (_b = options == null ? void 0 : options.tenantId) != null ? _b : this.config.tenantId,
625
+ actor: (_c = options == null ? void 0 : options.actor) != null ? _c : this.defaultActor ? {
626
+ actor_id: this.defaultActor.actorId,
627
+ actor_type: this.defaultActor.actorType
628
+ } : void 0,
629
+ subject: options == null ? void 0 : options.subject,
630
+ compliance: options == null ? void 0 : options.compliance,
631
+ payload
632
+ };
633
+ this.queue.push(envelope);
634
+ if (this.queue.length >= this.config.batchSize) {
635
+ void this.flush();
636
+ }
637
+ }
638
+ /**
639
+ * Force an immediate flush of all buffered events.
640
+ * Returns a promise that resolves when the flush completes.
641
+ */
642
+ flush() {
643
+ return __async(this, null, function* () {
644
+ if (this.queue.length === 0 || this.isFlushing) {
645
+ return;
646
+ }
647
+ this.isFlushing = true;
648
+ const batch = this.queue.splice(0, this.config.batchSize);
649
+ try {
650
+ yield this.sendBatch(batch);
651
+ } catch (e) {
652
+ this.queue.unshift(...batch);
653
+ } finally {
654
+ this.isFlushing = false;
655
+ }
656
+ if (this.queue.length >= this.config.batchSize) {
657
+ yield this.flush();
658
+ }
659
+ });
660
+ }
661
+ /**
662
+ * Gracefully shut down the tracker. Flushes remaining events and
663
+ * clears the flush interval.
664
+ */
665
+ shutdown() {
666
+ return __async(this, null, function* () {
667
+ this.isShutdown = true;
668
+ this.stopFlushInterval();
669
+ yield this.flush();
670
+ });
671
+ }
672
+ /**
673
+ * Send a batch of events to the Truth API with exponential backoff retry.
674
+ */
675
+ sendBatch(batch) {
676
+ return __async(this, null, function* () {
677
+ let lastError;
678
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
679
+ try {
680
+ const response = yield fetch(`${this.apiUrl}/api/events/ingest`, {
681
+ method: "POST",
682
+ headers: {
683
+ "Content-Type": "application/json",
684
+ Authorization: `Bearer ${this.config.apiKey}`
685
+ },
686
+ body: JSON.stringify({ events: batch })
687
+ });
688
+ if (response.ok) {
689
+ return;
690
+ }
691
+ if (response.status >= 400 && response.status < 500 && response.status !== 429) {
692
+ return;
693
+ }
694
+ lastError = new Error(
695
+ `HTTP ${response.status}: ${response.statusText}`
696
+ );
697
+ } catch (error) {
698
+ lastError = error;
699
+ }
700
+ if (attempt < MAX_RETRIES) {
701
+ const delay = BASE_RETRY_DELAY_MS * __pow(2, attempt);
702
+ const jitter = Math.random() * delay * 0.5;
703
+ yield sleep(delay + jitter);
704
+ }
705
+ }
706
+ throw lastError;
707
+ });
708
+ }
709
+ startFlushInterval() {
710
+ if (this.config.flushIntervalMs > 0) {
711
+ this.flushTimer = setInterval(() => {
712
+ void this.flush();
713
+ }, this.config.flushIntervalMs);
714
+ if (typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
715
+ this.flushTimer.unref();
716
+ }
717
+ }
718
+ }
719
+ stopFlushInterval() {
720
+ if (this.flushTimer !== null) {
721
+ clearInterval(this.flushTimer);
722
+ this.flushTimer = null;
723
+ }
724
+ }
725
+ registerShutdownHooks() {
726
+ if (typeof globalThis.process !== "undefined" && globalThis.process.on) {
727
+ const shutdownHandler = () => {
728
+ void this.shutdown();
729
+ };
730
+ globalThis.process.on("beforeExit", shutdownHandler);
731
+ globalThis.process.on("SIGTERM", shutdownHandler);
732
+ }
733
+ }
734
+ };
735
+ function sleep(ms) {
736
+ return new Promise((resolve) => setTimeout(resolve, ms));
737
+ }
738
+
739
+ // src/client.ts
740
+ var CONVEX_URLS = {
741
+ local: "http://localhost:3210",
742
+ staging: "https://staging-truth.convex.cloud",
743
+ uat: "https://uat-truth.convex.cloud",
744
+ production: "https://truth.convex.cloud"
745
+ };
746
+ var TruthClient = class {
747
+ constructor(config) {
748
+ var _a, _b, _c, _d, _e, _f, _g;
749
+ const convexUrl = (_b = (_a = config.convexUrl) != null ? _a : CONVEX_URLS[config.environment]) != null ? _b : CONVEX_URLS.local;
750
+ this.convex = new import_browser.ConvexHttpClient(convexUrl);
751
+ this.tracker = new Tracker({
752
+ apiKey: config.apiKey,
753
+ environment: config.environment,
754
+ source: (_c = config.source) != null ? _c : "unknown",
755
+ sourceVersion: (_d = config.sourceVersion) != null ? _d : "unknown",
756
+ tenantId: (_e = config.tenantId) != null ? _e : "",
757
+ batchSize: (_f = config.batchSize) != null ? _f : DEFAULT_BATCH_SIZE,
758
+ flushIntervalMs: (_g = config.flushIntervalMs) != null ? _g : DEFAULT_FLUSH_INTERVAL_MS,
759
+ apiBaseUrl: config.apiBaseUrl
760
+ });
761
+ const apiUrl = this.tracker.apiUrl;
762
+ this.patients = new PatientResource(this.convex);
763
+ this.appointments = new AppointmentResource(this.convex);
764
+ this.ehr = new EhrResource(apiUrl);
765
+ this.messages = new MessagesResource(apiUrl);
766
+ }
767
+ /**
768
+ * The resolved Truth API base URL for this environment.
769
+ * Use this when making HTTP calls to Truth's proxy endpoints
770
+ * (e.g., EHR proxy, messages proxy).
771
+ *
772
+ * @example
773
+ * ```ts
774
+ * const url = `${truth.apiBaseUrl}/api/ehr/elation/patients/123`;
775
+ * ```
776
+ */
777
+ get apiBaseUrl() {
778
+ return this.tracker.apiUrl;
779
+ }
780
+ /**
781
+ * Track an event. Fire-and-forget -- the event is buffered internally
782
+ * and flushed in batches to the Truth API.
783
+ *
784
+ * @example
785
+ * ```ts
786
+ * truth.track('conversation.message_sent.v1', {
787
+ * channel: 'sms',
788
+ * direction: 'outbound',
789
+ * message_chars: 140,
790
+ * has_attachment: false,
791
+ * provider_system: 'dialpad',
792
+ * });
793
+ * ```
794
+ */
795
+ track(eventType, payload, options) {
796
+ this.tracker.track(eventType, payload, options);
797
+ }
798
+ /**
799
+ * Set the default actor context for all subsequent tracked events.
800
+ * Can be overridden per-event via TrackOptions.
801
+ *
802
+ * @example
803
+ * ```ts
804
+ * truth.identify('user_123', 'user');
805
+ * ```
806
+ */
807
+ identify(actorId, actorType) {
808
+ this.tracker.setActor({ actorId, actorType });
809
+ }
810
+ /**
811
+ * Flush all buffered events immediately. Returns a Promise that resolves
812
+ * when the flush completes. Use this for graceful shutdown.
813
+ *
814
+ * @example
815
+ * ```ts
816
+ * process.on('SIGTERM', async () => {
817
+ * await truth.flush();
818
+ * process.exit(0);
819
+ * });
820
+ * ```
821
+ */
822
+ flush() {
823
+ return __async(this, null, function* () {
824
+ yield this.tracker.flush();
825
+ });
826
+ }
827
+ /**
828
+ * Gracefully shut down the client. Flushes all pending events and
829
+ * releases resources.
830
+ */
831
+ destroy() {
832
+ return __async(this, null, function* () {
833
+ yield this.tracker.shutdown();
834
+ });
835
+ }
836
+ };
837
+
838
+ // src/tracking/events.ts
839
+ var CONVERSATION_EVENTS = {
840
+ created: "conversation.created.v1",
841
+ messageSent: "conversation.message_sent.v1",
842
+ messageReceived: "conversation.message_received.v1",
843
+ markedRead: "conversation.marked_read.v1",
844
+ attachmentUploaded: "conversation.attachment_uploaded.v1",
845
+ attachmentDownloaded: "conversation.attachment_downloaded.v1"
846
+ };
847
+ var CALL_EVENTS = {
848
+ initiated: "call.initiated.v1",
849
+ connected: "call.connected.v1",
850
+ ended: "call.ended.v1",
851
+ missed: "call.missed.v1"
852
+ };
853
+ var TASK_EVENTS = {
854
+ created: "task.created.v1",
855
+ assigned: "task.assigned.v1",
856
+ statusChanged: "task.status_changed.v1"
857
+ };
858
+ var REMINDER_EVENTS = {
859
+ scheduled: "reminder.scheduled.v1",
860
+ triggered: "reminder.triggered.v1"
861
+ };
862
+ var TRANSLATION_EVENTS = {
863
+ requested: "translation.requested.v1",
864
+ completed: "translation.completed.v1"
865
+ };
866
+ var NOTIFICATION_EVENTS = {
867
+ sent: "notification.sent.v1",
868
+ delivered: "notification.delivered.v1",
869
+ opened: "notification.opened.v1"
870
+ };
871
+ var PROVIDER_EVENTS = {
872
+ syncStarted: "provider.sync_started.v1",
873
+ syncSucceeded: "provider.sync_succeeded.v1",
874
+ syncFailed: "provider.sync_failed.v1"
875
+ };
876
+ var AUTH_EVENTS = {
877
+ loginSucceeded: "auth.login_succeeded.v1",
878
+ loginFailed: "auth.login_failed.v1"
879
+ };
880
+ var SECURITY_EVENTS = {
881
+ accessDenied: "security.access_denied.v1"
882
+ };
883
+ var EVENT_TYPES = {
884
+ conversation: CONVERSATION_EVENTS,
885
+ call: CALL_EVENTS,
886
+ task: TASK_EVENTS,
887
+ reminder: REMINDER_EVENTS,
888
+ translation: TRANSLATION_EVENTS,
889
+ notification: NOTIFICATION_EVENTS,
890
+ provider: PROVIDER_EVENTS,
891
+ auth: AUTH_EVENTS,
892
+ security: SECURITY_EVENTS
893
+ };
894
+
895
+ // src/types/config.ts
896
+ var ENVIRONMENTS = {
897
+ local: "local",
898
+ staging: "staging",
899
+ uat: "uat",
900
+ production: "production"
901
+ };
902
+ // Annotate the CommonJS export names for ESM import in node:
903
+ 0 && (module.exports = {
904
+ AUTH_EVENTS,
905
+ AppointmentResource,
906
+ CALL_EVENTS,
907
+ CONVERSATION_EVENTS,
908
+ DialpadProxyError,
909
+ DialpadResource,
910
+ ENVIRONMENTS,
911
+ EVENT_TYPES,
912
+ EhrProviderProxy,
913
+ EhrProxyError,
914
+ EhrResource,
915
+ MessagesResource,
916
+ NOTIFICATION_EVENTS,
917
+ PROVIDER_EVENTS,
918
+ PatientResource,
919
+ REMINDER_EVENTS,
920
+ SECURITY_EVENTS,
921
+ TASK_EVENTS,
922
+ TRANSLATION_EVENTS,
923
+ Tracker,
924
+ TruthClient,
925
+ generateUuidV7
926
+ });
927
+ //# sourceMappingURL=index.js.map