@hipnation-truth/sdk 0.7.2 → 0.7.4

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/react.mjs DELETED
@@ -1,968 +0,0 @@
1
- "use client";
2
- var __pow = Math.pow;
3
- var __async = (__this, __arguments, generator) => {
4
- return new Promise((resolve, reject) => {
5
- var fulfilled = (value) => {
6
- try {
7
- step(generator.next(value));
8
- } catch (e) {
9
- reject(e);
10
- }
11
- };
12
- var rejected = (value) => {
13
- try {
14
- step(generator.throw(value));
15
- } catch (e) {
16
- reject(e);
17
- }
18
- };
19
- var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
20
- step((generator = generator.apply(__this, __arguments)).next());
21
- });
22
- };
23
-
24
- // src/react/conversations.ts
25
- import { useQuery } from "convex/react";
26
- import { makeFunctionReference } from "convex/server";
27
- var conversationsListForUserRef = makeFunctionReference("conversations:listForUser");
28
- var conversationsGetUnreadTotalForUserRef = makeFunctionReference("conversations:getUnreadTotalForUser");
29
- var conversationsGetByPhonePairRef = makeFunctionReference("conversations:getByPhonePair");
30
- var conversationMessagesGetByConversationIdRef = makeFunctionReference("conversationMessages:getByConversationId");
31
- var SKIP = "skip";
32
- function toResult(value, skipped) {
33
- if (skipped) {
34
- return { data: void 0, loading: false, error: void 0 };
35
- }
36
- return {
37
- data: value,
38
- loading: value === void 0,
39
- error: void 0
40
- };
41
- }
42
- function useConversations(filters) {
43
- const skipped = !filters.userId;
44
- const result = useQuery(
45
- conversationsListForUserRef,
46
- skipped ? SKIP : {
47
- userId: filters.userId,
48
- limit: filters.limit
49
- }
50
- );
51
- return toResult(result, skipped);
52
- }
53
- function useConversationByPhonePair(phonePair) {
54
- const skipped = !phonePair;
55
- const result = useQuery(
56
- conversationsGetByPhonePairRef,
57
- skipped ? SKIP : { phonePair }
58
- );
59
- return toResult(result, skipped);
60
- }
61
- function useMessages(conversationId, options) {
62
- const skipped = !conversationId;
63
- const result = useQuery(
64
- conversationMessagesGetByConversationIdRef,
65
- skipped ? SKIP : {
66
- conversationId,
67
- limit: options == null ? void 0 : options.limit
68
- }
69
- );
70
- return toResult(result, skipped);
71
- }
72
- function useUnreadCount(userId) {
73
- const skipped = !userId;
74
- const result = useQuery(
75
- conversationsGetUnreadTotalForUserRef,
76
- skipped ? SKIP : { userId }
77
- );
78
- return toResult(result, skipped);
79
- }
80
-
81
- // src/react/hooks.ts
82
- import { useQuery as useQuery2 } from "convex/react";
83
- import { useEffect } from "react";
84
- import { makeFunctionReference as makeFunctionReference2 } from "convex/server";
85
- var patientsListRef = makeFunctionReference2("patients:list");
86
- var patientsGetRef = makeFunctionReference2("patients:get");
87
- var patientsByElationIdRef = makeFunctionReference2("patients:getByElationId");
88
- var patientsByHintIdRef = makeFunctionReference2("patients:getByHintId");
89
- var appointmentsListRef = makeFunctionReference2("appointments:list");
90
- var appointmentsGetRef = makeFunctionReference2("appointments:get");
91
- var appointmentsByElationIdRef = makeFunctionReference2("appointments:getByElationId");
92
- function usePatients(options) {
93
- return useQuery2(patientsListRef, options != null ? options : {});
94
- }
95
- function usePatient(id) {
96
- return useQuery2(patientsGetRef, { id });
97
- }
98
- function usePatientByElationId(elationId) {
99
- return useQuery2(patientsByElationIdRef, {
100
- elationId
101
- });
102
- }
103
- function usePatientByHintId(hintId) {
104
- return useQuery2(patientsByHintIdRef, {
105
- hintId
106
- });
107
- }
108
- function useAppointments(options) {
109
- return useQuery2(
110
- appointmentsListRef,
111
- options != null ? options : {}
112
- );
113
- }
114
- function useAppointment(id) {
115
- return useQuery2(appointmentsGetRef, { id });
116
- }
117
- function useAppointmentByElationId(elationId) {
118
- return useQuery2(appointmentsByElationIdRef, {
119
- elationId
120
- });
121
- }
122
- var physiciansGetByElationIdsRef = makeFunctionReference2("physicians:getByElationIds");
123
- var physiciansGetByElationIdRef = makeFunctionReference2("physicians:getByElationId");
124
- function usePhysiciansByElationIds(ids) {
125
- return useQuery2(
126
- physiciansGetByElationIdsRef,
127
- ids && ids.length > 0 ? { ids } : "skip"
128
- );
129
- }
130
- function usePhysicianByElationId(id) {
131
- return useQuery2(
132
- physiciansGetByElationIdRef,
133
- id !== void 0 ? { id } : "skip"
134
- );
135
- }
136
- var medicationsByPatientRef = makeFunctionReference2("medicalRecords:getMedicationsByElationPatient");
137
- var problemsByPatientRef = makeFunctionReference2("medicalRecords:getProblemsByElationPatient");
138
- var allergiesByPatientRef = makeFunctionReference2("medicalRecords:getAllergiesByElationPatient");
139
- var appointmentsByPatientRef = makeFunctionReference2("medicalRecords:getAppointmentsByElationPatient");
140
- function usePatientMedical(elationId, options) {
141
- const medications = useQuery2(
142
- medicationsByPatientRef,
143
- elationId !== void 0 ? { elationPatientId: elationId } : "skip"
144
- );
145
- const problems = useQuery2(
146
- problemsByPatientRef,
147
- elationId !== void 0 ? { elationPatientId: elationId } : "skip"
148
- );
149
- const allergies = useQuery2(
150
- allergiesByPatientRef,
151
- elationId !== void 0 ? { elationPatientId: elationId } : "skip"
152
- );
153
- const appointments = useQuery2(
154
- appointmentsByPatientRef,
155
- elationId !== void 0 ? { elationPatientId: elationId } : "skip"
156
- );
157
- useEffect(() => {
158
- if (elationId === void 0 || (options == null ? void 0 : options.skipRefresh)) {
159
- return;
160
- }
161
- const apiBaseUrl = options == null ? void 0 : options.apiBaseUrl;
162
- const apiKey = options == null ? void 0 : options.apiKey;
163
- if (!apiBaseUrl || !apiKey) {
164
- return;
165
- }
166
- const controller = new AbortController();
167
- void fetch(`${apiBaseUrl}/api/patients/medical/refresh`, {
168
- method: "POST",
169
- headers: {
170
- "Content-Type": "application/json",
171
- "X-API-Key": apiKey
172
- },
173
- body: JSON.stringify({ elationId }),
174
- signal: controller.signal
175
- }).catch(() => {
176
- });
177
- return () => controller.abort();
178
- }, [elationId, options == null ? void 0 : options.apiBaseUrl, options == null ? void 0 : options.apiKey, options == null ? void 0 : options.skipRefresh]);
179
- return { medications, problems, allergies, appointments };
180
- }
181
- var elationPatientByIdRef = makeFunctionReference2("elationPatients:getByElationId");
182
- var hintPatientByIdRef = makeFunctionReference2("hintPatients:getByHintId");
183
- var pharmacyByNcpdpRef = makeFunctionReference2("elationPharmacies:getByNcpdpId");
184
- var patientPhotoByIdRef = makeFunctionReference2("elationPatientPhotos:getByElationPatientId");
185
- function usePatientBasic(input, options) {
186
- var _a, _b;
187
- const elationRow = useQuery2(
188
- elationPatientByIdRef,
189
- input.elationId !== void 0 ? { elationId: input.elationId } : "skip"
190
- );
191
- const hintRow = useQuery2(
192
- hintPatientByIdRef,
193
- input.hintId !== void 0 ? { hintId: input.hintId } : "skip"
194
- );
195
- useEffect(() => {
196
- if (options == null ? void 0 : options.skipRefresh) {
197
- return;
198
- }
199
- if (!input.hintId && input.elationId === void 0) {
200
- return;
201
- }
202
- const apiBaseUrl = options == null ? void 0 : options.apiBaseUrl;
203
- const apiKey = options == null ? void 0 : options.apiKey;
204
- if (!apiBaseUrl || !apiKey) {
205
- return;
206
- }
207
- const controller = new AbortController();
208
- void fetch(`${apiBaseUrl}/api/patients/basic/refresh`, {
209
- method: "POST",
210
- headers: {
211
- "Content-Type": "application/json",
212
- "X-API-Key": apiKey
213
- },
214
- body: JSON.stringify({
215
- hintId: input.hintId,
216
- elationId: input.elationId
217
- }),
218
- signal: controller.signal
219
- }).catch(() => {
220
- });
221
- return () => controller.abort();
222
- }, [
223
- input.hintId,
224
- input.elationId,
225
- options == null ? void 0 : options.apiBaseUrl,
226
- options == null ? void 0 : options.apiKey,
227
- options == null ? void 0 : options.skipRefresh
228
- ]);
229
- const elationPatient = elationRow === void 0 ? void 0 : elationRow === null ? null : (_a = elationRow.raw) != null ? _a : null;
230
- const hintPatient = hintRow === void 0 ? void 0 : hintRow === null ? null : (_b = hintRow.raw) != null ? _b : null;
231
- const elationLoading = input.elationId !== void 0 && elationRow === void 0;
232
- const hintLoading = input.hintId !== void 0 && hintRow === void 0;
233
- return {
234
- elationPatient,
235
- hintPatient,
236
- elationRow,
237
- hintRow,
238
- loading: elationLoading || hintLoading
239
- };
240
- }
241
- function usePharmacyByNcpdpId(ncpdpId) {
242
- return useQuery2(
243
- pharmacyByNcpdpRef,
244
- ncpdpId ? { ncpdpId } : "skip"
245
- );
246
- }
247
- function usePatientPhoto(elationId, options) {
248
- const photo = useQuery2(
249
- patientPhotoByIdRef,
250
- elationId !== void 0 ? { elationPatientId: elationId } : "skip"
251
- );
252
- useEffect(() => {
253
- if (options == null ? void 0 : options.skipRefresh) {
254
- return;
255
- }
256
- if (elationId === void 0) {
257
- return;
258
- }
259
- const apiBaseUrl = options == null ? void 0 : options.apiBaseUrl;
260
- const apiKey = options == null ? void 0 : options.apiKey;
261
- if (!apiBaseUrl || !apiKey) {
262
- return;
263
- }
264
- const controller = new AbortController();
265
- void fetch(`${apiBaseUrl}/api/patients/photo/refresh`, {
266
- method: "POST",
267
- headers: {
268
- "Content-Type": "application/json",
269
- "X-API-Key": apiKey
270
- },
271
- body: JSON.stringify({ elationId }),
272
- signal: controller.signal
273
- }).catch(() => {
274
- });
275
- return () => controller.abort();
276
- }, [elationId, options == null ? void 0 : options.apiBaseUrl, options == null ? void 0 : options.apiKey, options == null ? void 0 : options.skipRefresh]);
277
- return photo;
278
- }
279
- var messagesByPhonesRef = makeFunctionReference2("conversationMessages:getByPhones");
280
- var messagesByConversationIdRef = makeFunctionReference2("conversationMessages:getByConversationId");
281
- function useConversationMessages(input, options) {
282
- const hasPair = !!input.phoneA && !!input.phoneB;
283
- const byPair = useQuery2(
284
- messagesByPhonesRef,
285
- hasPair ? {
286
- phoneA: input.phoneA,
287
- phoneB: input.phoneB,
288
- limit: options == null ? void 0 : options.limit
289
- } : "skip"
290
- );
291
- const byConvo = useQuery2(
292
- messagesByConversationIdRef,
293
- !hasPair && input.conversationId ? { conversationId: input.conversationId, limit: options == null ? void 0 : options.limit } : "skip"
294
- );
295
- return hasPair ? byPair : byConvo;
296
- }
297
-
298
- // src/react/notifications.ts
299
- import { useCallback, useEffect as useEffect2, useRef, useState } from "react";
300
-
301
- // src/web-push.ts
302
- function isWebPushSupported() {
303
- return typeof window !== "undefined" && "serviceWorker" in navigator && "PushManager" in window;
304
- }
305
- function registerServiceWorker(path = "/truth-sw.js") {
306
- return __async(this, null, function* () {
307
- return navigator.serviceWorker.register(path);
308
- });
309
- }
310
- function subscribeToPush(registration, vapidPublicKey) {
311
- return __async(this, null, function* () {
312
- const existing = yield registration.pushManager.getSubscription();
313
- if (existing) {
314
- return existing;
315
- }
316
- return registration.pushManager.subscribe({
317
- userVisibleOnly: true,
318
- applicationServerKey: urlBase64ToUint8Array(
319
- vapidPublicKey
320
- )
321
- });
322
- });
323
- }
324
- function subscriptionToJSON(sub) {
325
- var _a, _b, _c, _d;
326
- const json = sub.toJSON();
327
- return {
328
- endpoint: sub.endpoint,
329
- keys: {
330
- p256dh: (_b = (_a = json.keys) == null ? void 0 : _a.p256dh) != null ? _b : "",
331
- auth: (_d = (_c = json.keys) == null ? void 0 : _c.auth) != null ? _d : ""
332
- }
333
- };
334
- }
335
- function urlBase64ToUint8Array(base64String) {
336
- const padding = "=".repeat((4 - base64String.length % 4) % 4);
337
- const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
338
- const rawData = atob(base64);
339
- const outputArray = new Uint8Array(rawData.length);
340
- for (let i = 0; i < rawData.length; ++i) {
341
- outputArray[i] = rawData.charCodeAt(i);
342
- }
343
- return outputArray;
344
- }
345
-
346
- // src/react/notifications.ts
347
- function loadExpo() {
348
- return __async(this, null, function* () {
349
- try {
350
- return yield import(
351
- /* @vite-ignore */
352
- /* webpackIgnore: true */
353
- // @ts-ignore — expo-notifications is an optional peer dep
354
- "expo-notifications"
355
- );
356
- } catch (e) {
357
- return null;
358
- }
359
- });
360
- }
361
- function useNotifications(options) {
362
- var _a;
363
- const [permissionStatus, setPermissionStatus] = useState("unknown");
364
- const [devicePushToken, setDevicePushToken] = useState(null);
365
- const expoRef = useRef(null);
366
- const isWebRef = useRef(false);
367
- const vapidKeyRef = useRef((_a = options.vapidPublicKey) != null ? _a : null);
368
- useEffect2(() => {
369
- let mounted = true;
370
- void (() => __async(null, null, function* () {
371
- var _a2;
372
- const expo = yield loadExpo();
373
- if (!mounted) {
374
- return;
375
- }
376
- expoRef.current = expo;
377
- if (expo) {
378
- try {
379
- const perm = yield expo.getPermissionsAsync();
380
- if (!mounted) {
381
- return;
382
- }
383
- setPermissionStatus(mapStatus(perm == null ? void 0 : perm.status));
384
- } catch (e) {
385
- setPermissionStatus("unknown");
386
- }
387
- return;
388
- }
389
- if (isWebPushSupported()) {
390
- isWebRef.current = true;
391
- if (!vapidKeyRef.current) {
392
- try {
393
- const res = yield fetch(
394
- `${options.apiBaseUrl}/api/notifications/vapid-key`,
395
- {
396
- headers: {
397
- Accept: "application/json",
398
- "X-API-Key": options.apiKey
399
- }
400
- }
401
- );
402
- if (res.ok) {
403
- const data = yield res.json();
404
- vapidKeyRef.current = (_a2 = data.vapidPublicKey) != null ? _a2 : null;
405
- }
406
- } catch (e) {
407
- }
408
- }
409
- if (!mounted) {
410
- return;
411
- }
412
- const webPerm = typeof Notification !== "undefined" ? Notification.permission : "default";
413
- setPermissionStatus(
414
- webPerm === "granted" ? "granted" : webPerm === "denied" ? "denied" : "undetermined"
415
- );
416
- } else {
417
- setPermissionStatus("unknown");
418
- }
419
- }))();
420
- return () => {
421
- mounted = false;
422
- };
423
- }, [options.apiBaseUrl, options.apiKey]);
424
- const register = useCallback(() => __async(null, null, function* () {
425
- var _a2, _b;
426
- if (!options.userId) {
427
- return { ok: false, reason: "missing_userId" };
428
- }
429
- if (isWebRef.current) {
430
- const vapidKey = vapidKeyRef.current;
431
- if (!vapidKey) {
432
- return { ok: false, reason: "no_vapid_key" };
433
- }
434
- const webPerm = yield Notification.requestPermission();
435
- setPermissionStatus(
436
- webPerm === "granted" ? "granted" : webPerm === "denied" ? "denied" : "undetermined"
437
- );
438
- if (webPerm !== "granted") {
439
- return { ok: false, reason: "permission_denied" };
440
- }
441
- try {
442
- const swPath = (_a2 = options.serviceWorkerPath) != null ? _a2 : "/truth-sw.js";
443
- const registration = yield registerServiceWorker(swPath);
444
- yield navigator.serviceWorker.ready;
445
- const subscription = yield subscribeToPush(registration, vapidKey);
446
- const subJSON = subscriptionToJSON(subscription);
447
- setDevicePushToken(subscription.endpoint);
448
- const res2 = yield fetch(
449
- `${options.apiBaseUrl}/api/notifications/devices/register`,
450
- {
451
- method: "POST",
452
- headers: {
453
- "Content-Type": "application/json",
454
- "X-API-Key": options.apiKey
455
- },
456
- body: JSON.stringify({
457
- userId: options.userId,
458
- platform: "web",
459
- webPushSubscription: subJSON,
460
- appVersion: options.appVersion,
461
- locale: navigator.language,
462
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
463
- })
464
- }
465
- );
466
- if (!res2.ok) {
467
- const text = yield res2.text().catch(() => "");
468
- return {
469
- ok: false,
470
- reason: `register_failed_${res2.status}: ${text.slice(0, 120)}`
471
- };
472
- }
473
- return { ok: true };
474
- } catch (err) {
475
- return {
476
- ok: false,
477
- reason: `web_push_error: ${err instanceof Error ? err.message : String(err)}`
478
- };
479
- }
480
- }
481
- const expo = (_b = expoRef.current) != null ? _b : yield loadExpo();
482
- expoRef.current = expo;
483
- if (!expo) {
484
- return { ok: false, reason: "expo_notifications_missing" };
485
- }
486
- let perm = yield expo.getPermissionsAsync();
487
- if ((perm == null ? void 0 : perm.status) !== "granted") {
488
- perm = yield expo.requestPermissionsAsync();
489
- }
490
- setPermissionStatus(mapStatus(perm == null ? void 0 : perm.status));
491
- if ((perm == null ? void 0 : perm.status) !== "granted") {
492
- return { ok: false, reason: "permission_denied" };
493
- }
494
- const tokenResp = yield expo.getDevicePushTokenAsync();
495
- const nativeToken = tokenResp == null ? void 0 : tokenResp.data;
496
- const platform = detectPlatform(tokenResp == null ? void 0 : tokenResp.type);
497
- if (!nativeToken || platform !== "ios" && platform !== "android") {
498
- return { ok: false, reason: "no_native_token" };
499
- }
500
- setDevicePushToken(nativeToken);
501
- const res = yield fetch(
502
- `${options.apiBaseUrl}/api/notifications/devices/register`,
503
- {
504
- method: "POST",
505
- headers: {
506
- "Content-Type": "application/json",
507
- "X-API-Key": options.apiKey
508
- },
509
- body: JSON.stringify({
510
- userId: options.userId,
511
- platform,
512
- nativeToken,
513
- appVersion: options.appVersion,
514
- locale: typeof navigator !== "undefined" ? navigator.language : void 0,
515
- timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
516
- })
517
- }
518
- );
519
- if (!res.ok) {
520
- const text = yield res.text().catch(() => "");
521
- return {
522
- ok: false,
523
- reason: `register_failed_${res.status}: ${text.slice(0, 120)}`
524
- };
525
- }
526
- return { ok: true };
527
- }), [
528
- options.apiBaseUrl,
529
- options.apiKey,
530
- options.userId,
531
- options.appVersion,
532
- options.serviceWorkerPath
533
- ]);
534
- const unregister = useCallback(() => __async(null, null, function* () {
535
- if (!devicePushToken) {
536
- return;
537
- }
538
- yield fetch(`${options.apiBaseUrl}/api/notifications/devices/unregister`, {
539
- method: "POST",
540
- headers: {
541
- "Content-Type": "application/json",
542
- "X-API-Key": options.apiKey
543
- },
544
- body: JSON.stringify({ nativeToken: devicePushToken })
545
- }).catch(() => {
546
- });
547
- setDevicePushToken(null);
548
- }), [options.apiBaseUrl, options.apiKey, devicePushToken]);
549
- const addReceivedListener = useCallback(
550
- (listener) => {
551
- if (isWebRef.current) {
552
- if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
553
- return () => {
554
- };
555
- }
556
- const handler = (event) => {
557
- var _a2;
558
- if (((_a2 = event.data) == null ? void 0 : _a2.type) === "TRUTH_PUSH_RECEIVED") {
559
- listener(event.data.payload);
560
- }
561
- };
562
- navigator.serviceWorker.addEventListener("message", handler);
563
- return () => navigator.serviceWorker.removeEventListener("message", handler);
564
- }
565
- const expo = expoRef.current;
566
- if (!(expo == null ? void 0 : expo.addNotificationReceivedListener)) {
567
- return () => {
568
- };
569
- }
570
- const sub = expo.addNotificationReceivedListener(listener);
571
- return () => {
572
- var _a2;
573
- return (_a2 = sub.remove) == null ? void 0 : _a2.call(sub);
574
- };
575
- },
576
- []
577
- );
578
- const addResponseListener = useCallback(
579
- (listener) => {
580
- if (isWebRef.current) {
581
- if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
582
- return () => {
583
- };
584
- }
585
- const handler = (event) => {
586
- var _a2;
587
- if (((_a2 = event.data) == null ? void 0 : _a2.type) === "TRUTH_PUSH_TAPPED") {
588
- listener(event.data.payload);
589
- }
590
- };
591
- navigator.serviceWorker.addEventListener("message", handler);
592
- return () => navigator.serviceWorker.removeEventListener("message", handler);
593
- }
594
- const expo = expoRef.current;
595
- if (!(expo == null ? void 0 : expo.addNotificationResponseReceivedListener)) {
596
- return () => {
597
- };
598
- }
599
- const sub = expo.addNotificationResponseReceivedListener(listener);
600
- return () => {
601
- var _a2;
602
- return (_a2 = sub.remove) == null ? void 0 : _a2.call(sub);
603
- };
604
- },
605
- []
606
- );
607
- const getBadgeCount = useCallback(() => __async(null, null, function* () {
608
- var _a2;
609
- const expo = expoRef.current;
610
- if (!(expo == null ? void 0 : expo.getBadgeCountAsync)) {
611
- return 0;
612
- }
613
- return (_a2 = yield expo.getBadgeCountAsync()) != null ? _a2 : 0;
614
- }), []);
615
- const setBadgeCount = useCallback((count) => __async(null, null, function* () {
616
- const expo = expoRef.current;
617
- if (!(expo == null ? void 0 : expo.setBadgeCountAsync)) {
618
- return;
619
- }
620
- yield expo.setBadgeCountAsync(count);
621
- }), []);
622
- const autoRegister = options.autoRegister !== false;
623
- useEffect2(() => {
624
- if (!autoRegister) {
625
- return;
626
- }
627
- if (permissionStatus !== "granted") {
628
- return;
629
- }
630
- if (devicePushToken) {
631
- return;
632
- }
633
- if (!options.userId) {
634
- return;
635
- }
636
- void register();
637
- }, [
638
- autoRegister,
639
- permissionStatus,
640
- devicePushToken,
641
- options.userId,
642
- register
643
- ]);
644
- return {
645
- permissionStatus,
646
- devicePushToken,
647
- register,
648
- unregister,
649
- addReceivedListener,
650
- addResponseListener,
651
- getBadgeCount,
652
- setBadgeCount
653
- };
654
- }
655
- function mapStatus(status) {
656
- if (status === "granted") {
657
- return "granted";
658
- }
659
- if (status === "denied") {
660
- return "denied";
661
- }
662
- if (status === "undetermined") {
663
- return "undetermined";
664
- }
665
- return "unknown";
666
- }
667
- function detectPlatform(tokenType) {
668
- if (tokenType === "apns") {
669
- return "ios";
670
- }
671
- if (tokenType === "fcm") {
672
- return "android";
673
- }
674
- if (tokenType === "web") {
675
- return "web";
676
- }
677
- return "unknown";
678
- }
679
-
680
- // src/react/provider.ts
681
- import { ConvexProvider, ConvexReactClient } from "convex/react";
682
- import { createElement, useMemo } from "react";
683
- var CONVEX_URLS = {
684
- local: "https://courteous-duck-623.convex.cloud",
685
- staging: "https://courteous-duck-623.convex.cloud",
686
- stg: "https://courteous-duck-623.convex.cloud",
687
- sandbox: "https://courteous-duck-623.convex.cloud",
688
- uat: "https://gallant-gecko-217.convex.cloud",
689
- production: "https://gallant-gecko-217.convex.cloud"
690
- };
691
- function resolveConvexUrl(environment, override) {
692
- var _a;
693
- if (override) {
694
- return override;
695
- }
696
- const env = environment != null ? environment : "sandbox";
697
- return (_a = CONVEX_URLS[env]) != null ? _a : CONVEX_URLS.sandbox;
698
- }
699
- function TruthProvider({
700
- environment = "sandbox",
701
- convexUrl,
702
- children
703
- }) {
704
- const url = resolveConvexUrl(environment, convexUrl);
705
- const client = useMemo(() => new ConvexReactClient(url), [url]);
706
- return createElement(ConvexProvider, { client }, children);
707
- }
708
-
709
- // src/react/tracking.ts
710
- import { createContext, createElement as createElement2, useContext, useMemo as useMemo2 } from "react";
711
-
712
- // src/tracking/tracker.ts
713
- function generateUuidV7() {
714
- const now = Date.now();
715
- const timeBytes = new Uint8Array(6);
716
- let ts = now;
717
- for (let i = 5; i >= 0; i--) {
718
- timeBytes[i] = ts & 255;
719
- ts = Math.floor(ts / 256);
720
- }
721
- const randomBytes = new Uint8Array(10);
722
- if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.getRandomValues) {
723
- globalThis.crypto.getRandomValues(randomBytes);
724
- } else {
725
- for (let i = 0; i < 10; i++) {
726
- randomBytes[i] = Math.floor(Math.random() * 256);
727
- }
728
- }
729
- const bytes = new Uint8Array(16);
730
- bytes.set(timeBytes, 0);
731
- bytes.set(randomBytes, 6);
732
- bytes[6] = bytes[6] & 15 | 112;
733
- bytes[8] = bytes[8] & 63 | 128;
734
- const hex = Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
735
- return [
736
- hex.slice(0, 8),
737
- hex.slice(8, 12),
738
- hex.slice(12, 16),
739
- hex.slice(16, 20),
740
- hex.slice(20, 32)
741
- ].join("-");
742
- }
743
- var API_URLS = {
744
- local: "http://localhost:3000",
745
- staging: "https://app.sandbox.communication-hub.com",
746
- stg: "https://app.sandbox.communication-hub.com",
747
- sandbox: "https://app.sandbox.communication-hub.com",
748
- uat: "https://app.truth.communication-hub.com",
749
- production: "https://app.truth.communication-hub.com"
750
- };
751
- var MAX_RETRIES = 3;
752
- var BASE_RETRY_DELAY_MS = 500;
753
- var Tracker = class {
754
- constructor(config) {
755
- this.queue = [];
756
- this.flushTimer = null;
757
- this.isFlushing = false;
758
- this.isShutdown = false;
759
- var _a, _b;
760
- this.config = config;
761
- this.apiUrl = (_b = (_a = config.apiBaseUrl) != null ? _a : API_URLS[config.environment]) != null ? _b : API_URLS.local;
762
- this.startFlushInterval();
763
- this.registerShutdownHooks();
764
- }
765
- /**
766
- * Set the default actor context for subsequent events.
767
- */
768
- setActor(actor) {
769
- this.defaultActor = actor;
770
- }
771
- /**
772
- * Enqueue a typed event for delivery. This is fire-and-forget from
773
- * the caller's perspective -- events are buffered and flushed in batches.
774
- */
775
- track(eventType, payload, options) {
776
- var _a, _b, _c;
777
- if (this.isShutdown) {
778
- return;
779
- }
780
- const now = (/* @__PURE__ */ new Date()).toISOString();
781
- const envelope = {
782
- event_id: generateUuidV7(),
783
- event_type: eventType,
784
- schema_version: 1,
785
- occurred_at: (_a = options == null ? void 0 : options.occurredAt) != null ? _a : now,
786
- received_at: now,
787
- source: this.config.source,
788
- source_version: this.config.sourceVersion,
789
- tenant_id: (_b = options == null ? void 0 : options.tenantId) != null ? _b : this.config.tenantId,
790
- actor: (_c = options == null ? void 0 : options.actor) != null ? _c : this.defaultActor ? {
791
- actor_id: this.defaultActor.actorId,
792
- actor_type: this.defaultActor.actorType
793
- } : void 0,
794
- subject: options == null ? void 0 : options.subject,
795
- compliance: options == null ? void 0 : options.compliance,
796
- payload
797
- };
798
- this.queue.push(envelope);
799
- if (this.queue.length >= this.config.batchSize) {
800
- void this.flush();
801
- }
802
- }
803
- /**
804
- * Force an immediate flush of all buffered events.
805
- * Returns a promise that resolves when the flush completes.
806
- */
807
- flush() {
808
- return __async(this, null, function* () {
809
- if (this.queue.length === 0 || this.isFlushing) {
810
- return;
811
- }
812
- this.isFlushing = true;
813
- const batch = this.queue.splice(0, this.config.batchSize);
814
- try {
815
- yield this.sendBatch(batch);
816
- } catch (e) {
817
- this.queue.unshift(...batch);
818
- } finally {
819
- this.isFlushing = false;
820
- }
821
- if (this.queue.length >= this.config.batchSize) {
822
- yield this.flush();
823
- }
824
- });
825
- }
826
- /**
827
- * Gracefully shut down the tracker. Flushes remaining events and
828
- * clears the flush interval.
829
- */
830
- shutdown() {
831
- return __async(this, null, function* () {
832
- this.isShutdown = true;
833
- this.stopFlushInterval();
834
- yield this.flush();
835
- });
836
- }
837
- /**
838
- * Send a batch of events to the Truth API with exponential backoff retry.
839
- */
840
- sendBatch(batch) {
841
- return __async(this, null, function* () {
842
- let lastError;
843
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
844
- try {
845
- const response = yield fetch(`${this.apiUrl}/api/events/ingest`, {
846
- method: "POST",
847
- headers: {
848
- "Content-Type": "application/json",
849
- "X-API-Key": this.config.apiKey
850
- },
851
- body: JSON.stringify({ events: batch })
852
- });
853
- if (response.ok) {
854
- return;
855
- }
856
- if (response.status >= 400 && response.status < 500 && response.status !== 429) {
857
- return;
858
- }
859
- lastError = new Error(
860
- `HTTP ${response.status}: ${response.statusText}`
861
- );
862
- } catch (error) {
863
- lastError = error;
864
- }
865
- if (attempt < MAX_RETRIES) {
866
- const delay = BASE_RETRY_DELAY_MS * __pow(2, attempt);
867
- const jitter = Math.random() * delay * 0.5;
868
- yield sleep(delay + jitter);
869
- }
870
- }
871
- throw lastError;
872
- });
873
- }
874
- startFlushInterval() {
875
- if (this.config.flushIntervalMs > 0) {
876
- this.flushTimer = setInterval(() => {
877
- void this.flush();
878
- }, this.config.flushIntervalMs);
879
- if (typeof this.flushTimer === "object" && "unref" in this.flushTimer) {
880
- this.flushTimer.unref();
881
- }
882
- }
883
- }
884
- stopFlushInterval() {
885
- if (this.flushTimer !== null) {
886
- clearInterval(this.flushTimer);
887
- this.flushTimer = null;
888
- }
889
- }
890
- registerShutdownHooks() {
891
- if (typeof globalThis.process !== "undefined" && globalThis.process.on) {
892
- const shutdownHandler = () => {
893
- void this.shutdown();
894
- };
895
- globalThis.process.on("beforeExit", shutdownHandler);
896
- globalThis.process.on("SIGTERM", shutdownHandler);
897
- }
898
- }
899
- };
900
- function sleep(ms) {
901
- return new Promise((resolve) => setTimeout(resolve, ms));
902
- }
903
-
904
- // src/react/tracking.ts
905
- var TruthTrackingContext = createContext(
906
- null
907
- );
908
- function TruthTrackingProvider({
909
- environment = "sandbox",
910
- source = "communication-hub.frontend",
911
- sourceVersion = "unknown",
912
- tenantId = "hipnation",
913
- apiKey = "",
914
- children
915
- }) {
916
- const value = useMemo2(() => {
917
- const tracker = new Tracker({
918
- apiKey,
919
- environment,
920
- source,
921
- sourceVersion,
922
- tenantId,
923
- batchSize: 10,
924
- flushIntervalMs: 5e3
925
- });
926
- return {
927
- track: (eventType, payload, options) => {
928
- tracker.track(eventType, payload, options);
929
- },
930
- identify: (actorId, actorType) => {
931
- tracker.setActor({ actorId, actorType });
932
- }
933
- };
934
- }, [apiKey, environment, source, sourceVersion, tenantId]);
935
- return createElement2(TruthTrackingContext.Provider, { value }, children);
936
- }
937
- function useTruth() {
938
- const ctx = useContext(TruthTrackingContext);
939
- if (!ctx) {
940
- throw new Error("useTruth must be used within a TruthTrackingProvider");
941
- }
942
- return ctx;
943
- }
944
- export {
945
- TruthProvider,
946
- TruthTrackingProvider,
947
- useAppointment,
948
- useAppointmentByElationId,
949
- useAppointments,
950
- useConversationByPhonePair,
951
- useConversationMessages,
952
- useConversations,
953
- useMessages,
954
- useNotifications,
955
- usePatient,
956
- usePatientBasic,
957
- usePatientByElationId,
958
- usePatientByHintId,
959
- usePatientMedical,
960
- usePatientPhoto,
961
- usePatients,
962
- usePharmacyByNcpdpId,
963
- usePhysicianByElationId,
964
- usePhysiciansByElationIds,
965
- useTruth,
966
- useUnreadCount
967
- };
968
- //# sourceMappingURL=react.mjs.map