@hipnation-truth/sdk 0.6.0 → 0.7.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.d.mts +201 -1
- package/dist/index.d.ts +201 -1
- package/dist/index.js +294 -3
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +286 -2
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +244 -1
- package/dist/react.d.ts +244 -1
- package/dist/react.js +530 -62
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +512 -49
- package/dist/react.mjs.map +1 -1
- package/package.json +6 -3
package/dist/index.mjs
CHANGED
|
@@ -572,6 +572,188 @@ var NotesResource = class {
|
|
|
572
572
|
}
|
|
573
573
|
};
|
|
574
574
|
|
|
575
|
+
// src/resources/notifications.ts
|
|
576
|
+
var NotificationsError = class extends Error {
|
|
577
|
+
constructor(operation, status, message) {
|
|
578
|
+
super(message != null ? message : `Notifications ${operation} failed (HTTP ${status})`);
|
|
579
|
+
this.name = "NotificationsError";
|
|
580
|
+
this.status = status;
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
var NotificationsResource = class {
|
|
584
|
+
constructor(apiBaseUrl, apiKey) {
|
|
585
|
+
this.baseUrl = apiBaseUrl;
|
|
586
|
+
this.apiKey = apiKey;
|
|
587
|
+
}
|
|
588
|
+
post(path, body) {
|
|
589
|
+
return __async(this, null, function* () {
|
|
590
|
+
const res = yield fetch(`${this.baseUrl}/api${path}`, {
|
|
591
|
+
method: "POST",
|
|
592
|
+
headers: {
|
|
593
|
+
"Content-Type": "application/json",
|
|
594
|
+
Accept: "application/json",
|
|
595
|
+
"X-API-Key": this.apiKey
|
|
596
|
+
},
|
|
597
|
+
body: JSON.stringify(body)
|
|
598
|
+
});
|
|
599
|
+
if (!res.ok) {
|
|
600
|
+
const text = yield res.text().catch(() => "");
|
|
601
|
+
throw new NotificationsError(path, res.status, text.slice(0, 200));
|
|
602
|
+
}
|
|
603
|
+
return yield res.json();
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
get(path, params) {
|
|
607
|
+
return __async(this, null, function* () {
|
|
608
|
+
const url = new URL(`${this.baseUrl}/api${path}`);
|
|
609
|
+
for (const [k, v] of Object.entries(params)) {
|
|
610
|
+
url.searchParams.set(k, v);
|
|
611
|
+
}
|
|
612
|
+
const res = yield fetch(url.toString(), {
|
|
613
|
+
method: "GET",
|
|
614
|
+
headers: {
|
|
615
|
+
Accept: "application/json",
|
|
616
|
+
"X-API-Key": this.apiKey
|
|
617
|
+
}
|
|
618
|
+
});
|
|
619
|
+
if (!res.ok) {
|
|
620
|
+
const text = yield res.text().catch(() => "");
|
|
621
|
+
throw new NotificationsError(path, res.status, text.slice(0, 200));
|
|
622
|
+
}
|
|
623
|
+
return yield res.json();
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
delete(path) {
|
|
627
|
+
return __async(this, null, function* () {
|
|
628
|
+
const res = yield fetch(`${this.baseUrl}/api${path}`, {
|
|
629
|
+
method: "DELETE",
|
|
630
|
+
headers: {
|
|
631
|
+
Accept: "application/json",
|
|
632
|
+
"X-API-Key": this.apiKey
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
if (!res.ok) {
|
|
636
|
+
const text = yield res.text().catch(() => "");
|
|
637
|
+
throw new NotificationsError(path, res.status, text.slice(0, 200));
|
|
638
|
+
}
|
|
639
|
+
return yield res.json();
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Register a device (or refresh its metadata) for push delivery.
|
|
644
|
+
* Safe to call repeatedly — the server dedupes by native token.
|
|
645
|
+
*/
|
|
646
|
+
registerDevice(input) {
|
|
647
|
+
return __async(this, null, function* () {
|
|
648
|
+
return this.post("/notifications/devices/register", input);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
/** Revoke a device — on sign-out or when the OS reports an invalid token. */
|
|
652
|
+
unregisterDevice(input) {
|
|
653
|
+
return __async(this, null, function* () {
|
|
654
|
+
return this.post("/notifications/devices/unregister", input);
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Send a push notification to every active device belonging to
|
|
659
|
+
* `userId`. Honors the user's notificationPreferences (quiet hours,
|
|
660
|
+
* DND, channel off) before publishing.
|
|
661
|
+
*/
|
|
662
|
+
send(input) {
|
|
663
|
+
return __async(this, null, function* () {
|
|
664
|
+
return this.post("/notifications/send", input);
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
/** Read a user's notification preferences. Returns defaults when no row exists. */
|
|
668
|
+
getPreferences(userId) {
|
|
669
|
+
return __async(this, null, function* () {
|
|
670
|
+
return this.get("/notifications/preferences", { userId });
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
updatePreferences(input) {
|
|
674
|
+
return __async(this, null, function* () {
|
|
675
|
+
return this.post("/notifications/preferences", input);
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Schedule a future push notification. Convex's native scheduler
|
|
680
|
+
* fires the send at `scheduledAt` and runs the same delivery
|
|
681
|
+
* pipeline as `send()` (preferences, devices, history audit).
|
|
682
|
+
*
|
|
683
|
+
* Throws `NotificationsError` with status 400 if `scheduledAt` is
|
|
684
|
+
* not strictly in the future.
|
|
685
|
+
*/
|
|
686
|
+
schedule(input) {
|
|
687
|
+
return __async(this, null, function* () {
|
|
688
|
+
return this.post("/notifications/schedule", input);
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Cancel a pending scheduled notification. Returns `cancelled: false`
|
|
693
|
+
* (no error) if the job has already executed, was previously
|
|
694
|
+
* cancelled, or no longer exists — `reason` describes which case.
|
|
695
|
+
*/
|
|
696
|
+
cancelScheduled(jobId) {
|
|
697
|
+
return __async(this, null, function* () {
|
|
698
|
+
return this.delete(`/notifications/schedule/${encodeURIComponent(jobId)}`);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* List scheduled notifications for a user — pending, executed,
|
|
703
|
+
* cancelled, or failed. Most-recent first. Default limit 100.
|
|
704
|
+
*/
|
|
705
|
+
listScheduled(userId, options) {
|
|
706
|
+
return __async(this, null, function* () {
|
|
707
|
+
const params = { userId };
|
|
708
|
+
if ((options == null ? void 0 : options.limit) !== void 0) {
|
|
709
|
+
params.limit = String(options.limit);
|
|
710
|
+
}
|
|
711
|
+
return this.get("/notifications/schedule", params);
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
getVapidKey() {
|
|
715
|
+
return __async(this, null, function* () {
|
|
716
|
+
try {
|
|
717
|
+
const result = yield this.get(
|
|
718
|
+
"/notifications/vapid-key",
|
|
719
|
+
{}
|
|
720
|
+
);
|
|
721
|
+
return result.vapidPublicKey;
|
|
722
|
+
} catch (e) {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
onPushReceived(callback) {
|
|
728
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
|
|
729
|
+
return () => {
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
const handler = (event) => {
|
|
733
|
+
var _a;
|
|
734
|
+
if (((_a = event.data) == null ? void 0 : _a.type) === "TRUTH_PUSH_RECEIVED") {
|
|
735
|
+
callback(event.data.payload);
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
navigator.serviceWorker.addEventListener("message", handler);
|
|
739
|
+
return () => navigator.serviceWorker.removeEventListener("message", handler);
|
|
740
|
+
}
|
|
741
|
+
onPushTapped(callback) {
|
|
742
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
|
|
743
|
+
return () => {
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
const handler = (event) => {
|
|
747
|
+
var _a;
|
|
748
|
+
if (((_a = event.data) == null ? void 0 : _a.type) === "TRUTH_PUSH_TAPPED") {
|
|
749
|
+
callback(event.data.payload);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
navigator.serviceWorker.addEventListener("message", handler);
|
|
753
|
+
return () => navigator.serviceWorker.removeEventListener("message", handler);
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
|
|
575
757
|
// src/resources/patient-details.ts
|
|
576
758
|
var PatientDetailsError = class extends Error {
|
|
577
759
|
constructor(operation, status, message) {
|
|
@@ -1137,6 +1319,65 @@ function sleep(ms) {
|
|
|
1137
1319
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1138
1320
|
}
|
|
1139
1321
|
|
|
1322
|
+
// src/web-push.ts
|
|
1323
|
+
function isWebPushSupported() {
|
|
1324
|
+
return typeof window !== "undefined" && "serviceWorker" in navigator && "PushManager" in window;
|
|
1325
|
+
}
|
|
1326
|
+
function registerServiceWorker(path = "/truth-sw.js") {
|
|
1327
|
+
return __async(this, null, function* () {
|
|
1328
|
+
return navigator.serviceWorker.register(path);
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
function subscribeToPush(registration, vapidPublicKey) {
|
|
1332
|
+
return __async(this, null, function* () {
|
|
1333
|
+
const existing = yield registration.pushManager.getSubscription();
|
|
1334
|
+
if (existing) {
|
|
1335
|
+
return existing;
|
|
1336
|
+
}
|
|
1337
|
+
return registration.pushManager.subscribe({
|
|
1338
|
+
userVisibleOnly: true,
|
|
1339
|
+
applicationServerKey: urlBase64ToUint8Array(
|
|
1340
|
+
vapidPublicKey
|
|
1341
|
+
)
|
|
1342
|
+
});
|
|
1343
|
+
});
|
|
1344
|
+
}
|
|
1345
|
+
function subscriptionToJSON(sub) {
|
|
1346
|
+
var _a, _b, _c, _d;
|
|
1347
|
+
const json = sub.toJSON();
|
|
1348
|
+
return {
|
|
1349
|
+
endpoint: sub.endpoint,
|
|
1350
|
+
keys: {
|
|
1351
|
+
p256dh: (_b = (_a = json.keys) == null ? void 0 : _a.p256dh) != null ? _b : "",
|
|
1352
|
+
auth: (_d = (_c = json.keys) == null ? void 0 : _c.auth) != null ? _d : ""
|
|
1353
|
+
}
|
|
1354
|
+
};
|
|
1355
|
+
}
|
|
1356
|
+
function urlBase64ToUint8Array(base64String) {
|
|
1357
|
+
const padding = "=".repeat((4 - base64String.length % 4) % 4);
|
|
1358
|
+
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
|
|
1359
|
+
const rawData = atob(base64);
|
|
1360
|
+
const outputArray = new Uint8Array(rawData.length);
|
|
1361
|
+
for (let i = 0; i < rawData.length; ++i) {
|
|
1362
|
+
outputArray[i] = rawData.charCodeAt(i);
|
|
1363
|
+
}
|
|
1364
|
+
return outputArray;
|
|
1365
|
+
}
|
|
1366
|
+
function onServiceWorkerMessage(type, callback) {
|
|
1367
|
+
if (typeof navigator === "undefined" || !("serviceWorker" in navigator)) {
|
|
1368
|
+
return () => {
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
const handler = (event) => {
|
|
1372
|
+
var _a;
|
|
1373
|
+
if (((_a = event.data) == null ? void 0 : _a.type) === type) {
|
|
1374
|
+
callback(event.data.payload);
|
|
1375
|
+
}
|
|
1376
|
+
};
|
|
1377
|
+
navigator.serviceWorker.addEventListener("message", handler);
|
|
1378
|
+
return () => navigator.serviceWorker.removeEventListener("message", handler);
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1140
1381
|
// src/client.ts
|
|
1141
1382
|
var CONVEX_URLS = {
|
|
1142
1383
|
local: "https://courteous-duck-623.convex.cloud",
|
|
@@ -1148,7 +1389,9 @@ var CONVEX_URLS = {
|
|
|
1148
1389
|
};
|
|
1149
1390
|
var TruthClient = class {
|
|
1150
1391
|
constructor(config) {
|
|
1151
|
-
|
|
1392
|
+
this._vapidPublicKey = null;
|
|
1393
|
+
this._webPushReady = null;
|
|
1394
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1152
1395
|
const convexUrl = (_b = (_a = config.convexUrl) != null ? _a : CONVEX_URLS[config.environment]) != null ? _b : CONVEX_URLS.local;
|
|
1153
1396
|
this.convex = new ConvexHttpClient(convexUrl);
|
|
1154
1397
|
this.tracker = new Tracker({
|
|
@@ -1177,6 +1420,40 @@ var TruthClient = class {
|
|
|
1177
1420
|
);
|
|
1178
1421
|
this.notes = new NotesResource(apiUrl, config.apiKey);
|
|
1179
1422
|
this.physicians = new PhysiciansResource(this.convex);
|
|
1423
|
+
this.notifications = new NotificationsResource(apiUrl, config.apiKey);
|
|
1424
|
+
this._serviceWorkerPath = (_h = config.serviceWorkerPath) != null ? _h : "/truth-sw.js";
|
|
1425
|
+
if (typeof window !== "undefined" && isWebPushSupported() && config.autoInitServiceWorker !== false) {
|
|
1426
|
+
this._webPushReady = this.initWebPush();
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
get vapidPublicKey() {
|
|
1430
|
+
return this._vapidPublicKey;
|
|
1431
|
+
}
|
|
1432
|
+
get webPushReady() {
|
|
1433
|
+
return this._webPushReady;
|
|
1434
|
+
}
|
|
1435
|
+
initWebPush() {
|
|
1436
|
+
return __async(this, null, function* () {
|
|
1437
|
+
try {
|
|
1438
|
+
const key = yield this.notifications.getVapidKey();
|
|
1439
|
+
if (!key) {
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
this._vapidPublicKey = key;
|
|
1443
|
+
const registration = yield registerServiceWorker(this._serviceWorkerPath);
|
|
1444
|
+
yield navigator.serviceWorker.ready;
|
|
1445
|
+
const subscription = yield subscribeToPush(registration, key);
|
|
1446
|
+
const subJSON = subscriptionToJSON(subscription);
|
|
1447
|
+
yield this.notifications.registerDevice({
|
|
1448
|
+
userId: "__pending__",
|
|
1449
|
+
platform: "web",
|
|
1450
|
+
webPushSubscription: subJSON,
|
|
1451
|
+
locale: navigator.language,
|
|
1452
|
+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
1453
|
+
});
|
|
1454
|
+
} catch (e) {
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1180
1457
|
}
|
|
1181
1458
|
/**
|
|
1182
1459
|
* The resolved Truth API base URL for this environment.
|
|
@@ -1333,6 +1610,8 @@ export {
|
|
|
1333
1610
|
NOTIFICATION_EVENTS,
|
|
1334
1611
|
NotesError,
|
|
1335
1612
|
NotesResource,
|
|
1613
|
+
NotificationsError,
|
|
1614
|
+
NotificationsResource,
|
|
1336
1615
|
PROVIDER_EVENTS,
|
|
1337
1616
|
PatientDetailsError,
|
|
1338
1617
|
PatientDetailsResource,
|
|
@@ -1348,6 +1627,11 @@ export {
|
|
|
1348
1627
|
TranslationError,
|
|
1349
1628
|
TranslationResource,
|
|
1350
1629
|
TruthClient,
|
|
1351
|
-
generateUuidV7
|
|
1630
|
+
generateUuidV7,
|
|
1631
|
+
isWebPushSupported,
|
|
1632
|
+
onServiceWorkerMessage,
|
|
1633
|
+
registerServiceWorker,
|
|
1634
|
+
subscribeToPush,
|
|
1635
|
+
subscriptionToJSON
|
|
1352
1636
|
};
|
|
1353
1637
|
//# sourceMappingURL=index.mjs.map
|