@ovok/core 0.4.0 → 0.4.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.
package/README.md CHANGED
@@ -17,6 +17,7 @@ Core TypeScript SDK for healthcare applications. Provides authentication, AI-FHI
17
17
  - 🧭 Runtime discovery of the backend FHIR and public API capabilities
18
18
  - 🧩 Typed accounts, projects, localization, and patient-observation APIs
19
19
  - 🏥 Typed CareHub patient, device, telemetry, notification, and monitoring APIs
20
+ - 🚨 Typed Signals thresholds, alerts, and project settings APIs
20
21
 
21
22
  ## Installation
22
23
 
@@ -151,9 +152,43 @@ const slots = await client.listPatientSlots({
151
152
  });
152
153
  ```
153
154
 
155
+ ## Signals APIs
156
+
157
+ The Signals client methods and hooks cover resident thresholds, the project's alert feed, and
158
+ Signals tenant settings. Ovok forwards observations for enabled residents to Signals on the
159
+ backend; continue saving readings through the existing Observation APIs and do not submit the
160
+ same readings to Signals separately.
161
+
162
+ ```ts
163
+ const thresholds = await client.getSignalsThresholds(patientId);
164
+ const updated = await client.setSignalsThreshold(patientId, "8867-4", {
165
+ low: 50,
166
+ high: 120,
167
+ reason: "Updated after review",
168
+ });
169
+ const alerts = await client.listSignalsAlerts({
170
+ status: "OPEN",
171
+ codes: ["8867-4"],
172
+ limit: 25,
173
+ });
174
+ await client.acknowledgeSignalsAlert(alerts.items[0].id);
175
+ const settings = await client.getSignalsSettings();
176
+ ```
177
+
178
+ Use `editable` and `source` from threshold rows to present the available controls, but let the
179
+ backend enforce permissions. When lowering alert sensitivity or removing a bound, provide a
180
+ non-empty `reason`; the backend decides whether the caller has the required capability. A
181
+ `shared` settings tenant is read-only. Signal errors are exposed as `SignalsError` values and can
182
+ be checked with `isSignalsError`.
183
+
184
+ `useSignalsAlerts` accumulates pages through `loadMore`. Check `hasMore` rather than the current
185
+ item count: access filtering can produce a short or empty page with another cursor. Query changes
186
+ and `reload` start again at the first page. The hook exposes `reload` so a screen can refresh its
187
+ alert list after `useAcknowledgeSignalsAlert` succeeds.
188
+
154
189
  ## CareHub APIs
155
190
 
156
- CareHub methods cover the verified `/v1/carehub/*` and `/v2/carehub/*` application routes for
191
+ CareHub methods cover the verified `/v1/slim/*` and `/v2/slim/*` application routes for
157
192
  patients, devices, telemetry, notifications, thresholds, trends, sleep, locations, zones, and
158
193
  PDF export. These routes require the backend's bearer session and CareHub permissions.
159
194
 
@@ -60,146 +60,146 @@ const query = (params) => {
60
60
  };
61
61
  const segment = (value) => encodeURIComponent(value);
62
62
  async function listCarehubPatients(params = {}) {
63
- return this.get(`/v1/carehub/patient${query(params)}`);
63
+ return this.get(`/v1/slim/patient${query(params)}`);
64
64
  }
65
65
  async function getCarehubPatient(patientId) {
66
- return this.get(`/v1/carehub/patient/${segment(patientId)}`);
66
+ return this.get(`/v1/slim/patient/${segment(patientId)}`);
67
67
  }
68
68
  async function getCarehubPatientEverything(patientId) {
69
- return this.get(`/v1/carehub/patient/${segment(patientId)}/$everything`);
69
+ return this.get(`/v1/slim/patient/${segment(patientId)}/$everything`);
70
70
  }
71
71
  async function getCarehubPatientCurrentVitals(patientId) {
72
- return this.get(`/v1/carehub/patient/${segment(patientId)}/current-vitals`);
72
+ return this.get(`/v1/slim/patient/${segment(patientId)}/current-vitals`);
73
73
  }
74
74
  async function createCarehubPatient(body) {
75
- return this.post("/v1/carehub/patient", body);
75
+ return this.post("/v1/slim/patient", body);
76
76
  }
77
77
  async function updateCarehubPatient(patientId, body) {
78
- return (await this.put(`/v1/carehub/patient/${segment(patientId)}`, body));
78
+ return (await this.put(`/v1/slim/patient/${segment(patientId)}`, body));
79
79
  }
80
80
  async function deleteCarehubPatient(patientId) {
81
- await this.delete(`/v1/carehub/patient/${segment(patientId)}`);
81
+ await this.delete(`/v1/slim/patient/${segment(patientId)}`);
82
82
  }
83
83
  async function listCarehubDevices(params = {}) {
84
- return this.get(`/v1/carehub/device${query(params)}`);
84
+ return this.get(`/v1/slim/device${query(params)}`);
85
85
  }
86
86
  async function getCarehubDevice(deviceId) {
87
- return this.get(`/v1/carehub/device/${segment(deviceId)}`);
87
+ return this.get(`/v1/slim/device/${segment(deviceId)}`);
88
88
  }
89
89
  async function createCarehubDevice(body) {
90
- return this.post("/v1/carehub/device", body);
90
+ return this.post("/v1/slim/device", body);
91
91
  }
92
92
  async function updateCarehubDevice(deviceId, body) {
93
- return (await this.put(`/v1/carehub/device/${segment(deviceId)}`, body));
93
+ return (await this.put(`/v1/slim/device/${segment(deviceId)}`, body));
94
94
  }
95
95
  async function deleteCarehubDevice(deviceId) {
96
- await this.delete(`/v1/carehub/device/${segment(deviceId)}`);
96
+ await this.delete(`/v1/slim/device/${segment(deviceId)}`);
97
97
  }
98
98
  async function assignCarehubDeviceToProject(body) {
99
- return (await this.put("/v1/carehub/device/assign/project", body));
99
+ return (await this.put("/v1/slim/device/assign/project", body));
100
100
  }
101
101
  async function listCarehubLiveDevices(params = {}) {
102
- return this.get(`/v1/carehub/device/live${query(params)}`);
102
+ return this.get(`/v1/slim/device/live${query(params)}`);
103
103
  }
104
104
  async function listCarehubLiveTelemetry(params = {}) {
105
- return this.get(`/v1/carehub/device/live/telemetry${query(params)}`);
105
+ return this.get(`/v1/slim/device/live/telemetry${query(params)}`);
106
106
  }
107
107
  async function getCarehubDeviceSleepStatus(deviceId) {
108
- return this.get(`/v1/carehub/device/live/cloudwatch/${segment(deviceId)}`);
108
+ return this.get(`/v1/slim/device/live/cloudwatch/${segment(deviceId)}`);
109
109
  }
110
110
  async function getCarehubDeviceTelemetry(deviceId) {
111
- return this.get(`/v1/carehub/telemetry/${segment(deviceId)}`);
111
+ return this.get(`/v1/slim/telemetry/${segment(deviceId)}`);
112
112
  }
113
113
  async function listCarehubEffectiveObservationIds() {
114
- return this.get("/v1/carehub/telemetry/effectiveIds");
114
+ return this.get("/v1/slim/telemetry/effectiveIds");
115
115
  }
116
116
  async function listCarehubLastObservationIds() {
117
- return this.get("/v1/carehub/telemetry/lastIds");
117
+ return this.get("/v1/slim/telemetry/lastIds");
118
118
  }
119
119
  async function getCarehubDeviceEffectiveObservationIds(deviceId) {
120
- return this.get(`/v1/carehub/telemetry/${segment(deviceId)}/effectiveIds`);
120
+ return this.get(`/v1/slim/telemetry/${segment(deviceId)}/effectiveIds`);
121
121
  }
122
122
  async function getCarehubDeviceLastObservationIds(deviceId) {
123
- return this.get(`/v1/carehub/telemetry/${segment(deviceId)}/lastIds`);
123
+ return this.get(`/v1/slim/telemetry/${segment(deviceId)}/lastIds`);
124
124
  }
125
125
  async function listCarehubNotifications(params = {}) {
126
- return this.get(`/v1/carehub/notifications${query(params)}`);
126
+ return this.get(`/v1/slim/notifications${query(params)}`);
127
127
  }
128
128
  async function getRecentCarehubNotifications(count) {
129
- return this.get(`/v1/carehub/notifications/recent${query({ count })}`);
129
+ return this.get(`/v1/slim/notifications/recent${query({ count })}`);
130
130
  }
131
131
  async function listCarehubOutOfBedNotifications(params = {}) {
132
- return this.get(`/v1/carehub/notifications/oob${query(params)}`);
132
+ return this.get(`/v1/slim/notifications/oob${query(params)}`);
133
133
  }
134
134
  async function listCarehubMedicalNotifications(params = {}) {
135
- return this.get(`/v1/carehub/notifications/medical${query(params)}`);
135
+ return this.get(`/v1/slim/notifications/medical${query(params)}`);
136
136
  }
137
137
  async function getRecentCarehubMedicalNotifications(count) {
138
- return this.get(`/v1/carehub/notifications/recent/medical${query({ count })}`);
138
+ return this.get(`/v1/slim/notifications/recent/medical${query({ count })}`);
139
139
  }
140
140
  async function getCarehubMedicalNotification(episodeId) {
141
- return this.get(`/v1/carehub/notifications/medical/${segment(episodeId)}`);
141
+ return this.get(`/v1/slim/notifications/medical/${segment(episodeId)}`);
142
142
  }
143
143
  async function acknowledgeCarehubMedicalNotification(episodeId) {
144
- return this.post(`/v1/carehub/notifications/medical/${segment(episodeId)}/ack`);
144
+ return this.post(`/v1/slim/notifications/medical/${segment(episodeId)}/ack`);
145
145
  }
146
146
  async function acknowledgeCarehubNotification(notificationId) {
147
- return (await this.patch(`/v1/carehub/notifications/${segment(notificationId)}/ack`, []));
147
+ return (await this.patch(`/v1/slim/notifications/${segment(notificationId)}/ack`, []));
148
148
  }
149
149
  async function getCarehubThreshold() {
150
- return this.get("/v1/carehub/threshold");
150
+ return this.get("/v1/slim/threshold");
151
151
  }
152
152
  async function updateCarehubThreshold(body) {
153
- return (await this.put("/v1/carehub/threshold", body));
153
+ return (await this.put("/v1/slim/threshold", body));
154
154
  }
155
155
  async function getCarehubPatientThreshold(patientId) {
156
- return this.get(`/v1/carehub/threshold/patient/${segment(patientId)}`);
156
+ return this.get(`/v1/slim/threshold/patient/${segment(patientId)}`);
157
157
  }
158
158
  async function updateCarehubPatientThreshold(patientId, body) {
159
- return (await this.put(`/v1/carehub/threshold/patient/${segment(patientId)}`, body));
159
+ return (await this.put(`/v1/slim/threshold/patient/${segment(patientId)}`, body));
160
160
  }
161
161
  async function getCarehubThresholdV2() {
162
- return this.get("/v2/carehub/threshold");
162
+ return this.get("/v2/slim/threshold");
163
163
  }
164
164
  async function updateCarehubThresholdV2(body) {
165
- return (await this.put("/v2/carehub/threshold", body));
165
+ return (await this.put("/v2/slim/threshold", body));
166
166
  }
167
167
  async function getCarehubPatientThresholdV2(patientId) {
168
- return this.get(`/v2/carehub/threshold/patient/${segment(patientId)}`);
168
+ return this.get(`/v2/slim/threshold/patient/${segment(patientId)}`);
169
169
  }
170
170
  async function updateCarehubPatientThresholdV2(patientId, body) {
171
- return (await this.put(`/v2/carehub/threshold/patient/${segment(patientId)}`, body));
171
+ return (await this.put(`/v2/slim/threshold/patient/${segment(patientId)}`, body));
172
172
  }
173
173
  async function getCarehubTrends(params) {
174
- return this.get(`/v1/carehub/trends/graphs${query(params)}`);
174
+ return this.get(`/v1/slim/trends/graphs${query(params)}`);
175
175
  }
176
176
  async function getCarehubSleepStatistics(params) {
177
- return this.get(`/v1/carehub/sleep/statistics${query(params)}`);
177
+ return this.get(`/v1/slim/sleep/statistics${query(params)}`);
178
178
  }
179
179
  async function listCarehubLocations(params = {}) {
180
- return this.get(`/v1/carehub/location${query(params)}`);
180
+ return this.get(`/v1/slim/location${query(params)}`);
181
181
  }
182
182
  async function getCarehubLocation(locationId) {
183
- return this.get(`/v1/carehub/location/${segment(locationId)}`);
183
+ return this.get(`/v1/slim/location/${segment(locationId)}`);
184
184
  }
185
185
  async function createCarehubLocation(body) {
186
- return this.post("/v1/carehub/location", body);
186
+ return this.post("/v1/slim/location", body);
187
187
  }
188
188
  async function updateCarehubLocation(locationId, body) {
189
- return (await this.put(`/v1/carehub/location/${segment(locationId)}`, body));
189
+ return (await this.put(`/v1/slim/location/${segment(locationId)}`, body));
190
190
  }
191
191
  async function deleteCarehubLocation(locationId) {
192
- await this.delete(`/v1/carehub/location/${segment(locationId)}`);
192
+ await this.delete(`/v1/slim/location/${segment(locationId)}`);
193
193
  }
194
194
  async function listCarehubZones(params = {}) {
195
- return this.get(`/v1/carehub/zones${query(params)}`);
195
+ return this.get(`/v1/slim/zones${query(params)}`);
196
196
  }
197
197
  async function getCarehubZone(zoneId) {
198
- return this.get(`/v1/carehub/zones/${segment(zoneId)}`);
198
+ return this.get(`/v1/slim/zones/${segment(zoneId)}`);
199
199
  }
200
200
  async function getCarehubZoneDeviceCounts(params = {}) {
201
- return this.get(`/v1/carehub/zones/device-counts${query(params)}`);
201
+ return this.get(`/v1/slim/zones/device-counts${query(params)}`);
202
202
  }
203
203
  async function exportCarehubPatientPdf(body) {
204
- return this.post("/v1/carehub/export/pdf", body);
204
+ return this.post("/v1/slim/export/pdf", body);
205
205
  }
@@ -14,6 +14,7 @@ import * as patientMethods from "./patient/methods";
14
14
  import * as patientSharingMethods from "./patient-sharing/methods";
15
15
  import * as platformMethods from "./platform/methods";
16
16
  import * as questionnaireResponseMethods from "./questionnaire-response/methods";
17
+ import * as signalsMethods from "./signals/methods";
17
18
  export declare const MAX_CONDITIONAL_OBSERVATION_ENTRIES = 8;
18
19
  export { MAX_OBSERVATION_BUNDLE_BYTES } from "./observation/observation-bundle-utils";
19
20
  export type SaveObservationsResult = {
@@ -59,7 +60,7 @@ export declare class OvokClient extends MedplumClient {
59
60
  */
60
61
  getSubscriptionManager(): SubscriptionManager;
61
62
  }
62
- type Methods = typeof authMethods & typeof observationMethods & typeof questionnaireResponseMethods & typeof aiMethods & typeof aiFhirMethods & typeof botMethods & typeof carehubMethods & typeof platformMethods & typeof experienceMethods & typeof patientMethods & typeof patientSharingMethods;
63
+ type Methods = typeof authMethods & typeof observationMethods & typeof questionnaireResponseMethods & typeof aiMethods & typeof aiFhirMethods & typeof botMethods & typeof carehubMethods & typeof platformMethods & typeof experienceMethods & typeof patientMethods & typeof patientSharingMethods & typeof signalsMethods;
63
64
  type Omitted<T> = Omit<T, "executeBot">;
64
65
  declare module "./ovok-client" {
65
66
  interface OvokClient extends Omitted<Methods> {
@@ -51,6 +51,8 @@ const patientMethods = __importStar(require("./patient/methods"));
51
51
  const patientSharingMethods = __importStar(require("./patient-sharing/methods"));
52
52
  const platformMethods = __importStar(require("./platform/methods"));
53
53
  const questionnaireResponseMethods = __importStar(require("./questionnaire-response/methods"));
54
+ const errors_1 = require("./signals/errors");
55
+ const signalsMethods = __importStar(require("./signals/methods"));
54
56
  exports.MAX_CONDITIONAL_OBSERVATION_ENTRIES = 8;
55
57
  var observation_bundle_utils_2 = require("./observation/observation-bundle-utils");
56
58
  Object.defineProperty(exports, "MAX_OBSERVATION_BUNDLE_BYTES", { enumerable: true, get: function () { return observation_bundle_utils_2.MAX_OBSERVATION_BUNDLE_BYTES; } });
@@ -75,6 +77,7 @@ class OvokClient extends core_1.MedplumClient {
75
77
  this.bindMethods(experienceMethods);
76
78
  this.bindMethods(patientMethods);
77
79
  this.bindMethods(patientSharingMethods);
80
+ this.bindMethods(signalsMethods, (error) => (0, errors_1.mapSignalsError)((0, rate_limit_error_1.mapRateLimitError)(error), { signalsRequest: true }));
78
81
  this.clientStorage = config.storage;
79
82
  this.offlineQueueOptions = (_b = config.offlineQueue) !== null && _b !== void 0 ? _b : {};
80
83
  }
@@ -141,7 +144,7 @@ class OvokClient extends core_1.MedplumClient {
141
144
  async executeObservationBundle(bundle) {
142
145
  return (0, observation_bundle_utils_1.executeObservationBundle)(bundle, (request) => this.executeBatch(request));
143
146
  }
144
- bindMethods(methods) {
147
+ bindMethods(methods, mapError = rate_limit_error_1.mapRateLimitError) {
145
148
  for (const [name, method] of Object.entries(methods)) {
146
149
  const boundMethod = method.bind(this);
147
150
  this[name] = async (...args) => {
@@ -149,7 +152,7 @@ class OvokClient extends core_1.MedplumClient {
149
152
  return await boundMethod(...args);
150
153
  }
151
154
  catch (error) {
152
- throw (0, rate_limit_error_1.mapRateLimitError)(error);
155
+ throw mapError(error);
153
156
  }
154
157
  };
155
158
  }
@@ -0,0 +1,19 @@
1
+ import { SignalsErrorCode, SignalsFieldError } from "./types";
2
+ type SignalsErrorOptions = {
3
+ cause?: unknown;
4
+ fieldErrors?: SignalsFieldError[];
5
+ };
6
+ /** A typed error returned by an Ovok Signals API route. */
7
+ export declare class SignalsError extends Error {
8
+ readonly code: SignalsErrorCode;
9
+ readonly status: number;
10
+ readonly fieldErrors: SignalsFieldError[];
11
+ readonly cause: unknown;
12
+ constructor(code: SignalsErrorCode, status: number, message: string, options?: SignalsErrorOptions);
13
+ }
14
+ export declare function isSignalsError(error: unknown): error is SignalsError;
15
+ /** Converts the Signals error envelope while leaving unrelated errors untouched. */
16
+ export declare function mapSignalsError(error: unknown, options?: {
17
+ signalsRequest?: boolean;
18
+ }): unknown;
19
+ export {};
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SignalsError = void 0;
4
+ exports.isSignalsError = isSignalsError;
5
+ exports.mapSignalsError = mapSignalsError;
6
+ const ovok_error_1 = require("../errors/ovok-error");
7
+ const SIGNALS_ERROR_STATUSES = {
8
+ signals_threshold_not_editable: 409,
9
+ signals_patient_not_enrolled: 409,
10
+ signals_conflict: 409,
11
+ signals_code_not_medical: 422,
12
+ signals_reason_required: 422,
13
+ signals_episodes_disabled: 409,
14
+ signals_bad_request: 422,
15
+ signals_shared_tenant: 409,
16
+ signals_audit_failed: 503,
17
+ signals_write_mismatch: 503,
18
+ signals_unavailable: 503,
19
+ signals_undo_failed: 503,
20
+ signals_forbidden: 403,
21
+ signals_not_found: 404,
22
+ validation_error: 422,
23
+ };
24
+ const SIGNALS_ERROR_CODES = new Set(Object.keys(SIGNALS_ERROR_STATUSES));
25
+ /** A typed error returned by an Ovok Signals API route. */
26
+ class SignalsError extends Error {
27
+ constructor(code, status, message, options = {}) {
28
+ var _a;
29
+ super(message);
30
+ this.name = "SignalsError";
31
+ this.code = code;
32
+ this.status = status;
33
+ this.fieldErrors = (_a = options.fieldErrors) !== null && _a !== void 0 ? _a : [];
34
+ if (options.cause !== undefined) {
35
+ Object.defineProperty(this, "cause", {
36
+ configurable: true,
37
+ enumerable: false,
38
+ value: options.cause,
39
+ writable: false,
40
+ });
41
+ }
42
+ }
43
+ }
44
+ exports.SignalsError = SignalsError;
45
+ function isSignalsError(error) {
46
+ return error instanceof SignalsError;
47
+ }
48
+ /** Converts the Signals error envelope while leaving unrelated errors untouched. */
49
+ function mapSignalsError(error, options = {}) {
50
+ if (isSignalsError(error)) {
51
+ return error;
52
+ }
53
+ const payload = responsePayload(error);
54
+ const code = responseCode(payload);
55
+ const parsed = (0, ovok_error_1.parseOvokError)(payload !== null && payload !== void 0 ? payload : error);
56
+ const message = (parsed.message === "[object Object]" || parsed.message === "undefined") &&
57
+ error instanceof Error
58
+ ? error.message
59
+ : parsed.message;
60
+ const status = responseStatus(error, payload);
61
+ if (code && SIGNALS_ERROR_CODES.has(code)) {
62
+ const knownCode = code;
63
+ const resolvedStatus = status !== null && status !== void 0 ? status : SIGNALS_ERROR_STATUSES[knownCode];
64
+ if (resolvedStatus !== undefined) {
65
+ return new SignalsError(knownCode, resolvedStatus, message, {
66
+ cause: error,
67
+ fieldErrors: parsed.fieldErrors,
68
+ });
69
+ }
70
+ }
71
+ if (status === 409 && options.signalsRequest && !code) {
72
+ return new SignalsError("signals_conflict", status, message, { cause: error });
73
+ }
74
+ if (status === 403 && !code) {
75
+ return new SignalsError("signals_forbidden", status, message, { cause: error });
76
+ }
77
+ if (status === 404 && !code) {
78
+ return new SignalsError("signals_not_found", status, message, { cause: error });
79
+ }
80
+ if (status === 422 && !code) {
81
+ const validationCode = /reason.*(required|missing|provide|must)|(?:required|missing).{0,20}reason/i.test(message)
82
+ ? "signals_reason_required"
83
+ : "validation_error";
84
+ return new SignalsError(validationCode, status, message, {
85
+ cause: error,
86
+ fieldErrors: parsed.fieldErrors,
87
+ });
88
+ }
89
+ return error;
90
+ }
91
+ function responsePayload(error) {
92
+ var _a, _b;
93
+ const root = asRecord(error);
94
+ const candidates = [
95
+ root === null || root === void 0 ? void 0 : root.body,
96
+ root === null || root === void 0 ? void 0 : root.data,
97
+ (_a = asRecord(root === null || root === void 0 ? void 0 : root.response)) === null || _a === void 0 ? void 0 : _a.data,
98
+ (_b = asRecord(root === null || root === void 0 ? void 0 : root.response)) === null || _b === void 0 ? void 0 : _b.body,
99
+ root === null || root === void 0 ? void 0 : root.outcome,
100
+ ];
101
+ for (const candidate of candidates) {
102
+ const parsed = parseJson(candidate);
103
+ if (parsed !== undefined) {
104
+ return parsed;
105
+ }
106
+ }
107
+ if (error instanceof Error) {
108
+ const parsed = parseJson(error.message);
109
+ if (parsed !== undefined) {
110
+ return parsed;
111
+ }
112
+ }
113
+ return error;
114
+ }
115
+ function responseCode(payload) {
116
+ var _a, _b, _c, _d, _e, _f;
117
+ const root = asRecord(payload);
118
+ const error = asRecord(root === null || root === void 0 ? void 0 : root.error);
119
+ const directCode = (_b = (_a = (typeof (root === null || root === void 0 ? void 0 : root.error) === "string" ? root.error : undefined)) !== null && _a !== void 0 ? _a : (typeof (root === null || root === void 0 ? void 0 : root.code) === "string" ? root.code : undefined)) !== null && _b !== void 0 ? _b : (typeof (error === null || error === void 0 ? void 0 : error.code) === "string" ? error.code : undefined);
120
+ if (directCode) {
121
+ return directCode;
122
+ }
123
+ const outcome = (_d = (_c = asRecord(root === null || root === void 0 ? void 0 : root.outcome)) !== null && _c !== void 0 ? _c : root) !== null && _d !== void 0 ? _d : {};
124
+ const issues = Array.isArray(outcome.issue) ? outcome.issue : [];
125
+ for (const issue of issues) {
126
+ const issueRecord = asRecord(issue);
127
+ const details = asRecord(issueRecord === null || issueRecord === void 0 ? void 0 : issueRecord.details);
128
+ const coding = Array.isArray(details === null || details === void 0 ? void 0 : details.coding) ? details.coding : [];
129
+ for (const entry of coding) {
130
+ const entryCode = (_e = asRecord(entry)) === null || _e === void 0 ? void 0 : _e.code;
131
+ if (typeof entryCode === "string" && entryCode.startsWith("signals_")) {
132
+ return entryCode;
133
+ }
134
+ }
135
+ const diagnostic = parseJson(issueRecord === null || issueRecord === void 0 ? void 0 : issueRecord.diagnostics);
136
+ const diagnosticRecord = asRecord(diagnostic);
137
+ const diagnosticCode = (_f = diagnosticRecord === null || diagnosticRecord === void 0 ? void 0 : diagnosticRecord.error) !== null && _f !== void 0 ? _f : diagnosticRecord === null || diagnosticRecord === void 0 ? void 0 : diagnosticRecord.code;
138
+ if (typeof diagnosticCode === "string") {
139
+ return diagnosticCode;
140
+ }
141
+ }
142
+ return undefined;
143
+ }
144
+ function responseStatus(error, payload) {
145
+ var _a, _b, _c, _d, _e, _f;
146
+ const root = asRecord(error);
147
+ const response = asRecord(root === null || root === void 0 ? void 0 : root.response);
148
+ const body = asRecord(payload);
149
+ const status = (_d = (_c = (_b = (_a = root === null || root === void 0 ? void 0 : root.status) !== null && _a !== void 0 ? _a : root === null || root === void 0 ? void 0 : root.statusCode) !== null && _b !== void 0 ? _b : response === null || response === void 0 ? void 0 : response.status) !== null && _c !== void 0 ? _c : body === null || body === void 0 ? void 0 : body.status) !== null && _d !== void 0 ? _d : body === null || body === void 0 ? void 0 : body.statusCode;
150
+ if (typeof status === "number") {
151
+ return status;
152
+ }
153
+ const outcome = (_e = asRecord(body === null || body === void 0 ? void 0 : body.outcome)) !== null && _e !== void 0 ? _e : body;
154
+ const issues = Array.isArray(outcome === null || outcome === void 0 ? void 0 : outcome.issue) ? outcome.issue : [];
155
+ for (const issue of issues) {
156
+ const issueCode = (_f = asRecord(issue)) === null || _f === void 0 ? void 0 : _f.code;
157
+ if (issueCode === "conflict") {
158
+ return 409;
159
+ }
160
+ if (issueCode === "forbidden") {
161
+ return 403;
162
+ }
163
+ if (issueCode === "not-found") {
164
+ return 404;
165
+ }
166
+ }
167
+ return undefined;
168
+ }
169
+ function parseJson(value) {
170
+ if (typeof value !== "string") {
171
+ return value;
172
+ }
173
+ try {
174
+ return JSON.parse(value);
175
+ }
176
+ catch (_a) {
177
+ return undefined;
178
+ }
179
+ }
180
+ function asRecord(value) {
181
+ return value && typeof value === "object"
182
+ ? value
183
+ : undefined;
184
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./errors";
2
+ export * from "./methods";
3
+ export * from "./types";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./errors"), exports);
18
+ __exportStar(require("./methods"), exports);
19
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,11 @@
1
+ import { OvokClient } from "../ovok-client";
2
+ import { SetSignalsThresholdBody, SignalsAlert, SignalsAlertsPage, SignalsAlertsQuery, SignalsSettingsPatch, SignalsSettingsResponse, SignalsThreshold, SignalsThresholds } from "./types";
3
+ export declare function getSignalsThresholds(this: OvokClient, patientId: string): Promise<SignalsThresholds>;
4
+ export declare function setSignalsThreshold(this: OvokClient, patientId: string, code: string, body: SetSignalsThresholdBody): Promise<SignalsThreshold>;
5
+ export declare function removeSignalsThreshold(this: OvokClient, patientId: string, code: string, reason: string): Promise<void>;
6
+ export declare function serializeSignalsAlertsQuery(query?: SignalsAlertsQuery): string;
7
+ export declare function listSignalsAlerts(this: OvokClient, query?: SignalsAlertsQuery): Promise<SignalsAlertsPage>;
8
+ export declare function getSignalsAlert(this: OvokClient, episodeId: string): Promise<SignalsAlert>;
9
+ export declare function acknowledgeSignalsAlert(this: OvokClient, episodeId: string): Promise<SignalsAlert>;
10
+ export declare function getSignalsSettings(this: OvokClient): Promise<SignalsSettingsResponse>;
11
+ export declare function updateSignalsSettings(this: OvokClient, patch: SignalsSettingsPatch, reason?: string | null): Promise<SignalsSettingsResponse>;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSignalsThresholds = getSignalsThresholds;
4
+ exports.setSignalsThreshold = setSignalsThreshold;
5
+ exports.removeSignalsThreshold = removeSignalsThreshold;
6
+ exports.serializeSignalsAlertsQuery = serializeSignalsAlertsQuery;
7
+ exports.listSignalsAlerts = listSignalsAlerts;
8
+ exports.getSignalsAlert = getSignalsAlert;
9
+ exports.acknowledgeSignalsAlert = acknowledgeSignalsAlert;
10
+ exports.getSignalsSettings = getSignalsSettings;
11
+ exports.updateSignalsSettings = updateSignalsSettings;
12
+ const segment = (value) => encodeURIComponent(value);
13
+ function getSignalsThresholds(patientId) {
14
+ return this.get(`/v1/signals/patients/${segment(patientId)}/thresholds`);
15
+ }
16
+ function setSignalsThreshold(patientId, code, body) {
17
+ return this.put(`/v1/signals/patients/${segment(patientId)}/thresholds/${segment(code)}`, body);
18
+ }
19
+ function removeSignalsThreshold(patientId, code, reason) {
20
+ return this.delete(`/v1/signals/patients/${segment(patientId)}/thresholds/${segment(code)}?reason=${segment(reason)}`);
21
+ }
22
+ function serializeSignalsAlertsQuery(query = {}) {
23
+ const params = [];
24
+ if (query.status !== undefined) {
25
+ params.push(["status", query.status]);
26
+ }
27
+ if (query.since !== undefined) {
28
+ params.push(["since", query.since]);
29
+ }
30
+ if (query.acknowledged !== undefined) {
31
+ params.push(["acknowledged", String(query.acknowledged)]);
32
+ }
33
+ if (query.codes !== undefined) {
34
+ params.push(["codes", query.codes.join(",")]);
35
+ }
36
+ if (query.limit !== undefined) {
37
+ params.push(["limit", String(query.limit)]);
38
+ }
39
+ if (query.cursor !== undefined) {
40
+ params.push(["cursor", query.cursor]);
41
+ }
42
+ return params
43
+ .map(([key, value]) => `${segment(key)}=${segment(value)}`)
44
+ .join("&");
45
+ }
46
+ function listSignalsAlerts(query = {}) {
47
+ const serialized = serializeSignalsAlertsQuery(query);
48
+ return this.get(`/v1/signals/alerts${serialized ? `?${serialized}` : ""}`);
49
+ }
50
+ function getSignalsAlert(episodeId) {
51
+ return this.get(`/v1/signals/alerts/${segment(episodeId)}`);
52
+ }
53
+ function acknowledgeSignalsAlert(episodeId) {
54
+ return this.post(`/v1/signals/alerts/${segment(episodeId)}/ack`);
55
+ }
56
+ function getSignalsSettings() {
57
+ return this.get("/v1/signals/settings");
58
+ }
59
+ function updateSignalsSettings(patch, reason) {
60
+ return this.patch("/v1/signals/settings", {
61
+ ...patch,
62
+ ...(reason !== undefined ? { reason } : {}),
63
+ });
64
+ }
@@ -0,0 +1,54 @@
1
+ import { ValidationError } from "../../types/ValidationError";
2
+ export type SignalsThresholdSource = "organization" | "individual" | "auto" | "unknown";
3
+ export type SignalsThreshold = {
4
+ code: string;
5
+ low: number | null;
6
+ high: number | null;
7
+ editable: boolean;
8
+ version: number;
9
+ updatedAt: string;
10
+ source: SignalsThresholdSource;
11
+ };
12
+ export type SignalsThresholds = {
13
+ patientId: string;
14
+ thresholds: SignalsThreshold[];
15
+ };
16
+ export type SetSignalsThresholdBody = {
17
+ low: number | null;
18
+ high: number | null;
19
+ reason?: string | null;
20
+ };
21
+ export type SignalsAlertStatus = "OPEN" | "CLOSED";
22
+ export type SignalsAlert = {
23
+ id: string;
24
+ patientId: string;
25
+ signalsPatientId: string;
26
+ code: string;
27
+ status: SignalsAlertStatus;
28
+ [key: string]: unknown;
29
+ };
30
+ export type SignalsAlertsQuery = {
31
+ status?: SignalsAlertStatus;
32
+ since?: string;
33
+ acknowledged?: boolean;
34
+ codes?: readonly string[];
35
+ limit?: number;
36
+ cursor?: string;
37
+ };
38
+ export type SignalsAlertsPage = {
39
+ items: SignalsAlert[];
40
+ nextCursor: string | null;
41
+ };
42
+ export type SignalsSettings = {
43
+ episodicAlerts: boolean;
44
+ synchronousMode: boolean;
45
+ throttleLimit: number | null;
46
+ };
47
+ export type SignalsTenantKind = "project" | "shared";
48
+ export type SignalsSettingsResponse = {
49
+ tenant: SignalsTenantKind;
50
+ settings: SignalsSettings;
51
+ };
52
+ export type SignalsSettingsPatch = Partial<SignalsSettings>;
53
+ export type SignalsErrorCode = "signals_threshold_not_editable" | "signals_patient_not_enrolled" | "signals_conflict" | "signals_code_not_medical" | "signals_reason_required" | "signals_episodes_disabled" | "signals_bad_request" | "signals_shared_tenant" | "signals_audit_failed" | "signals_write_mismatch" | "signals_unavailable" | "signals_undo_failed" | "signals_forbidden" | "signals_not_found" | "validation_error";
54
+ export type SignalsFieldError = ValidationError;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -93,53 +93,53 @@ exports.OVOK_CORE_API_REQUIREMENTS = {
93
93
  { method: "GET", path: "/fhir/Device/:id/telemetry/history", group: "Patient" },
94
94
  { method: "POST", path: "/fhir/Questionnaire/:id/$populate", group: "Questionnaire" },
95
95
  { method: "POST", path: "/fhir/QuestionnaireResponse/:id/$extract", group: "Questionnaire" },
96
- { method: "GET", path: "/v1/carehub/patient", group: "Carehub/Patient" },
97
- { method: "GET", path: "/v1/carehub/patient/:id", group: "Carehub/Patient" },
98
- { method: "GET", path: "/v1/carehub/patient/:id/$everything", group: "Carehub/Patient" },
99
- { method: "GET", path: "/v1/carehub/patient/:id/current-vitals", group: "Carehub/Patient" },
100
- { method: "POST", path: "/v1/carehub/patient", group: "Carehub/Patient" },
101
- { method: "PUT", path: "/v1/carehub/patient/:id", group: "Carehub/Patient" },
102
- { method: "DELETE", path: "/v1/carehub/patient/:id", group: "Carehub/Patient" },
103
- { method: "GET", path: "/v1/carehub/device", group: "Carehub/Device" },
104
- { method: "GET", path: "/v1/carehub/device/:id", group: "Carehub/Device" },
105
- { method: "POST", path: "/v1/carehub/device", group: "Carehub/Device" },
106
- { method: "PUT", path: "/v1/carehub/device/:id", group: "Carehub/Device" },
107
- { method: "DELETE", path: "/v1/carehub/device/:id", group: "Carehub/Device" },
108
- { method: "PUT", path: "/v1/carehub/device/assign/project", group: "Carehub/Device" },
109
- { method: "GET", path: "/v1/carehub/device/live", group: "Carehub/Device" },
110
- { method: "GET", path: "/v1/carehub/device/live/telemetry", group: "Carehub/Device" },
111
- { method: "GET", path: "/v1/carehub/device/live/cloudwatch/:deviceId", group: "Carehub/Device" },
112
- { method: "GET", path: "/v1/carehub/telemetry/:deviceId", group: "Carehub/Telemetry" },
113
- { method: "GET", path: "/v1/carehub/telemetry/effectiveIds", group: "Carehub/Telemetry" },
114
- { method: "GET", path: "/v1/carehub/telemetry/lastIds", group: "Carehub/Telemetry" },
115
- { method: "GET", path: "/v1/carehub/telemetry/:deviceId/effectiveIds", group: "Carehub/Telemetry" },
116
- { method: "GET", path: "/v1/carehub/telemetry/:deviceId/lastIds", group: "Carehub/Telemetry" },
117
- { method: "GET", path: "/v1/carehub/notifications", group: "Carehub/Notifications" },
118
- { method: "GET", path: "/v1/carehub/notifications/recent", group: "Carehub/Notifications" },
119
- { method: "GET", path: "/v1/carehub/notifications/oob", group: "Carehub/Notifications" },
120
- { method: "GET", path: "/v1/carehub/notifications/medical", group: "Carehub/Notifications" },
121
- { method: "GET", path: "/v1/carehub/notifications/recent/medical", group: "Carehub/Notifications" },
122
- { method: "GET", path: "/v1/carehub/notifications/medical/:episodeId", group: "Carehub/Notifications" },
123
- { method: "POST", path: "/v1/carehub/notifications/medical/:episodeId/ack", group: "Carehub/Notifications" },
124
- { method: "PATCH", path: "/v1/carehub/notifications/:id/ack", group: "Carehub/Notifications" },
125
- { method: "GET", path: "/v1/carehub/threshold", group: "Carehub/Threshold" },
126
- { method: "PUT", path: "/v1/carehub/threshold", group: "Carehub/Threshold" },
127
- { method: "GET", path: "/v1/carehub/threshold/patient/:patientId", group: "Carehub/Threshold" },
128
- { method: "PUT", path: "/v1/carehub/threshold/patient/:patientId", group: "Carehub/Threshold" },
129
- { method: "GET", path: "/v2/carehub/threshold", group: "Carehub/Threshold" },
130
- { method: "PUT", path: "/v2/carehub/threshold", group: "Carehub/Threshold" },
131
- { method: "GET", path: "/v2/carehub/threshold/patient/:patientId", group: "Carehub/Threshold" },
132
- { method: "PUT", path: "/v2/carehub/threshold/patient/:patientId", group: "Carehub/Threshold" },
133
- { method: "GET", path: "/v1/carehub/trends/graphs", group: "Carehub/Trends" },
134
- { method: "GET", path: "/v1/carehub/sleep/statistics", group: "Carehub/Sleep" },
135
- { method: "GET", path: "/v1/carehub/location", group: "Carehub/Location" },
136
- { method: "GET", path: "/v1/carehub/location/:id", group: "Carehub/Location" },
137
- { method: "POST", path: "/v1/carehub/location", group: "Carehub/Location" },
138
- { method: "PUT", path: "/v1/carehub/location/:id", group: "Carehub/Location" },
139
- { method: "DELETE", path: "/v1/carehub/location/:id", group: "Carehub/Location" },
140
- { method: "GET", path: "/v1/carehub/zones", group: "Carehub/Zones" },
141
- { method: "GET", path: "/v1/carehub/zones/:zoneId", group: "Carehub/Zones" },
142
- { method: "GET", path: "/v1/carehub/zones/device-counts", group: "Carehub/Zones" },
143
- { method: "POST", path: "/v1/carehub/export/pdf", group: "Carehub/Export" },
96
+ { method: "GET", path: "/v1/slim/patient", group: "Slim/Patient" },
97
+ { method: "GET", path: "/v1/slim/patient/:id", group: "Slim/Patient" },
98
+ { method: "GET", path: "/v1/slim/patient/:id/$everything", group: "Slim/Patient" },
99
+ { method: "GET", path: "/v1/slim/patient/:id/current-vitals", group: "Slim/Patient" },
100
+ { method: "POST", path: "/v1/slim/patient", group: "Slim/Patient" },
101
+ { method: "PUT", path: "/v1/slim/patient/:id", group: "Slim/Patient" },
102
+ { method: "DELETE", path: "/v1/slim/patient/:id", group: "Slim/Patient" },
103
+ { method: "GET", path: "/v1/slim/device", group: "Slim/Device" },
104
+ { method: "GET", path: "/v1/slim/device/:id", group: "Slim/Device" },
105
+ { method: "POST", path: "/v1/slim/device", group: "Slim/Device" },
106
+ { method: "PUT", path: "/v1/slim/device/:id", group: "Slim/Device" },
107
+ { method: "DELETE", path: "/v1/slim/device/:id", group: "Slim/Device" },
108
+ { method: "PUT", path: "/v1/slim/device/assign/project", group: "Slim/Device" },
109
+ { method: "GET", path: "/v1/slim/device/live", group: "Slim/Device" },
110
+ { method: "GET", path: "/v1/slim/device/live/telemetry", group: "Slim/Device" },
111
+ { method: "GET", path: "/v1/slim/device/live/cloudwatch/:deviceId", group: "Slim/Device" },
112
+ { method: "GET", path: "/v1/slim/telemetry/:deviceId", group: "Slim/Telemetry" },
113
+ { method: "GET", path: "/v1/slim/telemetry/effectiveIds", group: "Slim/Telemetry" },
114
+ { method: "GET", path: "/v1/slim/telemetry/lastIds", group: "Slim/Telemetry" },
115
+ { method: "GET", path: "/v1/slim/telemetry/:deviceId/effectiveIds", group: "Slim/Telemetry" },
116
+ { method: "GET", path: "/v1/slim/telemetry/:deviceId/lastIds", group: "Slim/Telemetry" },
117
+ { method: "GET", path: "/v1/slim/notifications", group: "Slim/Notifications" },
118
+ { method: "GET", path: "/v1/slim/notifications/recent", group: "Slim/Notifications" },
119
+ { method: "GET", path: "/v1/slim/notifications/oob", group: "Slim/Notifications" },
120
+ { method: "GET", path: "/v1/slim/notifications/medical", group: "Slim/Notifications" },
121
+ { method: "GET", path: "/v1/slim/notifications/recent/medical", group: "Slim/Notifications" },
122
+ { method: "GET", path: "/v1/slim/notifications/medical/:episodeId", group: "Slim/Notifications" },
123
+ { method: "POST", path: "/v1/slim/notifications/medical/:episodeId/ack", group: "Slim/Notifications" },
124
+ { method: "PATCH", path: "/v1/slim/notifications/:id/ack", group: "Slim/Notifications" },
125
+ { method: "GET", path: "/v1/slim/threshold", group: "Slim/Threshold" },
126
+ { method: "PUT", path: "/v1/slim/threshold", group: "Slim/Threshold" },
127
+ { method: "GET", path: "/v1/slim/threshold/patient/:patientId", group: "Slim/Threshold" },
128
+ { method: "PUT", path: "/v1/slim/threshold/patient/:patientId", group: "Slim/Threshold" },
129
+ { method: "GET", path: "/v2/slim/threshold", group: "Slim/Threshold" },
130
+ { method: "PUT", path: "/v2/slim/threshold", group: "Slim/Threshold" },
131
+ { method: "GET", path: "/v2/slim/threshold/patient/:patientId", group: "Slim/Threshold" },
132
+ { method: "PUT", path: "/v2/slim/threshold/patient/:patientId", group: "Slim/Threshold" },
133
+ { method: "GET", path: "/v1/slim/trends/graphs", group: "Slim/Trends" },
134
+ { method: "GET", path: "/v1/slim/sleep/statistics", group: "Slim/Sleep" },
135
+ { method: "GET", path: "/v1/slim/location", group: "Slim/Location" },
136
+ { method: "GET", path: "/v1/slim/location/:id", group: "Slim/Location" },
137
+ { method: "POST", path: "/v1/slim/location", group: "Slim/Location" },
138
+ { method: "PUT", path: "/v1/slim/location/:id", group: "Slim/Location" },
139
+ { method: "DELETE", path: "/v1/slim/location/:id", group: "Slim/Location" },
140
+ { method: "GET", path: "/v1/slim/zones", group: "Slim/Zones" },
141
+ { method: "GET", path: "/v1/slim/zones/:zoneId", group: "Slim/Zones" },
142
+ { method: "GET", path: "/v1/slim/zones/device-counts", group: "Slim/Zones" },
143
+ { method: "POST", path: "/v1/slim/export/pdf", group: "Slim/Export" },
144
144
  ],
145
145
  };
@@ -48,7 +48,7 @@ exports.OVOK_CORE_REQUIREMENTS = {
48
48
  description: "The FHIR surface a server must provide for the @ovok/core client library's own methods to function.",
49
49
  software: {
50
50
  name: "@ovok/core",
51
- version: "0.4.0",
51
+ version: "0.4.2",
52
52
  },
53
53
  fhirVersion: "4.0.1",
54
54
  format: ["json"],
@@ -11,3 +11,4 @@ export { useConsentSignature, useContent, useCreateVideoCallAppointment, useCrea
11
11
  export { useI18nextDocument, useLocales, useLocalizations, useUpdateI18nextDocument, useUpdateLocalization, } from "./platform-hooks";
12
12
  export { useCurrentPatientProfile, useExtractQuestionnaireObservations, usePatientAppointments, usePatientCarePlans, usePatientDevices, usePatientDeviceTelemetry, usePatientDeviceTelemetryHistory, usePatientMeasurement, usePatientMeasurementHistory, usePatientDashboard, usePatientLatestObservations, usePatientNotifications, usePatientQuestionnaireResponses, usePatientQuestionnaires, usePatientSchedules, usePatientSlots, usePopulateQuestionnaire, useSubmitQuestionnaireResponse, } from "./patient-hooks";
13
13
  export { useCreatePatientSharingInvite, usePatientSharing, usePatientSharingSettings, useRedeemPatientSharingInvite, useRevokePatientSharing, useUpdatePatientSharingSettings, } from "./patient-sharing-hooks";
14
+ export { useAcknowledgeSignalsAlert, useRemoveSignalsThreshold, useSetSignalsThreshold, useSignalsAlert, useSignalsAlerts, useSignalsSettings, useSignalsThresholds, useUpdateSignalsSettings, } from "./signals-hooks";
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  })();
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.usePatientMeasurement = exports.usePatientDeviceTelemetryHistory = exports.usePatientDeviceTelemetry = exports.usePatientDevices = exports.usePatientCarePlans = exports.usePatientAppointments = exports.useExtractQuestionnaireObservations = exports.useCurrentPatientProfile = exports.useUpdateLocalization = exports.useUpdateI18nextDocument = exports.useLocalizations = exports.useLocales = exports.useI18nextDocument = exports.useVideoCallAppointments = exports.useVideoCallAccess = exports.useDocuments = exports.useCreateVideoCallUserAccess = exports.useCreateVideoCallAppointment = exports.useContent = exports.useConsentSignature = exports.useRecentCarehubNotifications = exports.usePublicApiEndpoints = exports.useProjectSettings = exports.useProjectFeatures = exports.useOvokRequest = exports.useOvokMutation = exports.useCapabilityStatement = exports.useCarehubPatients = exports.useCarehubNotifications = exports.useCarehubDevices = exports.useCarehubDeviceTelemetry = exports.useAdministeredProjects = exports.useUrineTests = exports.useSaveMeasurement = exports.useObservations = exports.useEcgRecording = exports.observationsToMeasurements = exports.OvokProvider = exports.useClient = exports.useCachedBinaryUrl = exports.reactContext = exports.useSubscription = exports.useSearchResources = exports.useSearchOne = exports.useSearch = exports.useResource = exports.usePrevious = exports.useMedplumProfile = exports.useMedplumNavigate = exports.useMedplumContext = void 0;
38
- exports.useUpdatePatientSharingSettings = exports.useRevokePatientSharing = exports.useRedeemPatientSharingInvite = exports.usePatientSharingSettings = exports.usePatientSharing = exports.useCreatePatientSharingInvite = exports.useSubmitQuestionnaireResponse = exports.usePopulateQuestionnaire = exports.usePatientSlots = exports.usePatientSchedules = exports.usePatientQuestionnaires = exports.usePatientQuestionnaireResponses = exports.usePatientNotifications = exports.usePatientLatestObservations = exports.usePatientDashboard = exports.usePatientMeasurementHistory = void 0;
38
+ exports.useUpdateSignalsSettings = exports.useSignalsThresholds = exports.useSignalsSettings = exports.useSignalsAlerts = exports.useSignalsAlert = exports.useSetSignalsThreshold = exports.useRemoveSignalsThreshold = exports.useAcknowledgeSignalsAlert = exports.useUpdatePatientSharingSettings = exports.useRevokePatientSharing = exports.useRedeemPatientSharingInvite = exports.usePatientSharingSettings = exports.usePatientSharing = exports.useCreatePatientSharingInvite = exports.useSubmitQuestionnaireResponse = exports.usePopulateQuestionnaire = exports.usePatientSlots = exports.usePatientSchedules = exports.usePatientQuestionnaires = exports.usePatientQuestionnaireResponses = exports.usePatientNotifications = exports.usePatientLatestObservations = exports.usePatientDashboard = exports.usePatientMeasurementHistory = void 0;
39
39
  /*
40
40
  * The only React-dependent module in the package.
41
41
  *
@@ -130,3 +130,12 @@ Object.defineProperty(exports, "usePatientSharingSettings", { enumerable: true,
130
130
  Object.defineProperty(exports, "useRedeemPatientSharingInvite", { enumerable: true, get: function () { return patient_sharing_hooks_1.useRedeemPatientSharingInvite; } });
131
131
  Object.defineProperty(exports, "useRevokePatientSharing", { enumerable: true, get: function () { return patient_sharing_hooks_1.useRevokePatientSharing; } });
132
132
  Object.defineProperty(exports, "useUpdatePatientSharingSettings", { enumerable: true, get: function () { return patient_sharing_hooks_1.useUpdatePatientSharingSettings; } });
133
+ var signals_hooks_1 = require("./signals-hooks");
134
+ Object.defineProperty(exports, "useAcknowledgeSignalsAlert", { enumerable: true, get: function () { return signals_hooks_1.useAcknowledgeSignalsAlert; } });
135
+ Object.defineProperty(exports, "useRemoveSignalsThreshold", { enumerable: true, get: function () { return signals_hooks_1.useRemoveSignalsThreshold; } });
136
+ Object.defineProperty(exports, "useSetSignalsThreshold", { enumerable: true, get: function () { return signals_hooks_1.useSetSignalsThreshold; } });
137
+ Object.defineProperty(exports, "useSignalsAlert", { enumerable: true, get: function () { return signals_hooks_1.useSignalsAlert; } });
138
+ Object.defineProperty(exports, "useSignalsAlerts", { enumerable: true, get: function () { return signals_hooks_1.useSignalsAlerts; } });
139
+ Object.defineProperty(exports, "useSignalsSettings", { enumerable: true, get: function () { return signals_hooks_1.useSignalsSettings; } });
140
+ Object.defineProperty(exports, "useSignalsThresholds", { enumerable: true, get: function () { return signals_hooks_1.useSignalsThresholds; } });
141
+ Object.defineProperty(exports, "useUpdateSignalsSettings", { enumerable: true, get: function () { return signals_hooks_1.useUpdateSignalsSettings; } });
@@ -0,0 +1,34 @@
1
+ import { SetSignalsThresholdBody, SignalsAlert, SignalsAlertsQuery, SignalsSettingsPatch, SignalsSettingsResponse, SignalsThreshold, SignalsThresholds } from "../client/signals";
2
+ import { OvokMutationState, OvokRequestState } from "./api-hooks";
3
+ type SetSignalsThresholdParams = {
4
+ patientId: string;
5
+ code: string;
6
+ body: SetSignalsThresholdBody;
7
+ };
8
+ type RemoveSignalsThresholdParams = {
9
+ patientId: string;
10
+ code: string;
11
+ reason: string;
12
+ };
13
+ type UpdateSignalsSettingsParams = {
14
+ patch: SignalsSettingsPatch;
15
+ reason?: string | null;
16
+ };
17
+ type SignalsAlertsHookQuery = Omit<SignalsAlertsQuery, "cursor">;
18
+ export type SignalsAlertsHookState = {
19
+ items: SignalsAlert[];
20
+ loading: boolean;
21
+ error: Error | undefined;
22
+ reload: () => void;
23
+ hasMore: boolean;
24
+ loadMore: () => Promise<void>;
25
+ };
26
+ export declare const useSignalsThresholds: (patientId?: string) => OvokRequestState<SignalsThresholds>;
27
+ export declare const useSetSignalsThreshold: () => OvokMutationState<SetSignalsThresholdParams, SignalsThreshold>;
28
+ export declare const useRemoveSignalsThreshold: () => OvokMutationState<RemoveSignalsThresholdParams, void>;
29
+ export declare const useSignalsAlerts: (query?: SignalsAlertsHookQuery) => SignalsAlertsHookState;
30
+ export declare const useSignalsAlert: (episodeId?: string) => OvokRequestState<SignalsAlert>;
31
+ export declare const useAcknowledgeSignalsAlert: () => OvokMutationState<string, SignalsAlert>;
32
+ export declare const useSignalsSettings: () => OvokRequestState<SignalsSettingsResponse>;
33
+ export declare const useUpdateSignalsSettings: () => OvokMutationState<UpdateSignalsSettingsParams, SignalsSettingsResponse>;
34
+ export {};
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ "use client";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.useUpdateSignalsSettings = exports.useSignalsSettings = exports.useAcknowledgeSignalsAlert = exports.useSignalsAlert = exports.useSignalsAlerts = exports.useRemoveSignalsThreshold = exports.useSetSignalsThreshold = exports.useSignalsThresholds = void 0;
38
+ const React = __importStar(require("react"));
39
+ const api_hooks_1 = require("./api-hooks");
40
+ const index_1 = require("./index");
41
+ const useSignalsThresholds = (patientId) => {
42
+ const client = (0, index_1.useClient)();
43
+ const load = React.useCallback(() => client.getSignalsThresholds(patientId), [client, patientId]);
44
+ return (0, api_hooks_1.useOvokRequest)(load, patientId !== undefined);
45
+ };
46
+ exports.useSignalsThresholds = useSignalsThresholds;
47
+ const useSetSignalsThreshold = () => (0, api_hooks_1.useOvokMutation)((client, params) => client.setSignalsThreshold(params.patientId, params.code, params.body));
48
+ exports.useSetSignalsThreshold = useSetSignalsThreshold;
49
+ const useRemoveSignalsThreshold = () => (0, api_hooks_1.useOvokMutation)((client, params) => client.removeSignalsThreshold(params.patientId, params.code, params.reason));
50
+ exports.useRemoveSignalsThreshold = useRemoveSignalsThreshold;
51
+ const useSignalsAlerts = (query = {}) => {
52
+ const client = (0, index_1.useClient)();
53
+ const queryKey = JSON.stringify(query);
54
+ const querySnapshot = React.useMemo(() => JSON.parse(queryKey), [queryKey]);
55
+ const [items, setItems] = React.useState([]);
56
+ const [nextCursor, setNextCursor] = React.useState(null);
57
+ const [loading, setLoading] = React.useState(true);
58
+ const [error, setError] = React.useState();
59
+ const [revision, setRevision] = React.useState(0);
60
+ const generation = React.useRef(0);
61
+ const loadingRef = React.useRef(false);
62
+ React.useEffect(() => {
63
+ const requestGeneration = generation.current + 1;
64
+ generation.current = requestGeneration;
65
+ loadingRef.current = true;
66
+ setItems([]);
67
+ setNextCursor(null);
68
+ setError(undefined);
69
+ setLoading(true);
70
+ client
71
+ .listSignalsAlerts(querySnapshot)
72
+ .then((page) => {
73
+ if (generation.current === requestGeneration) {
74
+ setItems(page.items);
75
+ setNextCursor(page.nextCursor);
76
+ }
77
+ })
78
+ .catch((reason) => {
79
+ if (generation.current === requestGeneration) {
80
+ setError(toError(reason));
81
+ }
82
+ })
83
+ .finally(() => {
84
+ if (generation.current === requestGeneration) {
85
+ loadingRef.current = false;
86
+ setLoading(false);
87
+ }
88
+ });
89
+ return () => {
90
+ generation.current += 1;
91
+ };
92
+ }, [client, queryKey, querySnapshot, revision]);
93
+ const loadMore = React.useCallback(async () => {
94
+ if (nextCursor === null || loadingRef.current) {
95
+ return;
96
+ }
97
+ const requestGeneration = generation.current;
98
+ loadingRef.current = true;
99
+ setLoading(true);
100
+ setError(undefined);
101
+ try {
102
+ const page = await client.listSignalsAlerts({
103
+ ...querySnapshot,
104
+ cursor: nextCursor,
105
+ });
106
+ if (generation.current === requestGeneration) {
107
+ setItems((current) => [...current, ...page.items]);
108
+ setNextCursor(page.nextCursor);
109
+ }
110
+ }
111
+ catch (reason) {
112
+ if (generation.current === requestGeneration) {
113
+ setError(toError(reason));
114
+ }
115
+ }
116
+ finally {
117
+ if (generation.current === requestGeneration) {
118
+ loadingRef.current = false;
119
+ setLoading(false);
120
+ }
121
+ }
122
+ }, [client, nextCursor, querySnapshot]);
123
+ const reload = React.useCallback(() => {
124
+ setRevision((current) => current + 1);
125
+ }, []);
126
+ return {
127
+ items,
128
+ loading,
129
+ error,
130
+ reload,
131
+ hasMore: nextCursor !== null,
132
+ loadMore,
133
+ };
134
+ };
135
+ exports.useSignalsAlerts = useSignalsAlerts;
136
+ const useSignalsAlert = (episodeId) => {
137
+ const client = (0, index_1.useClient)();
138
+ const load = React.useCallback(() => client.getSignalsAlert(episodeId), [client, episodeId]);
139
+ return (0, api_hooks_1.useOvokRequest)(load, episodeId !== undefined);
140
+ };
141
+ exports.useSignalsAlert = useSignalsAlert;
142
+ const useAcknowledgeSignalsAlert = () => (0, api_hooks_1.useOvokMutation)((client, episodeId) => client.acknowledgeSignalsAlert(episodeId));
143
+ exports.useAcknowledgeSignalsAlert = useAcknowledgeSignalsAlert;
144
+ const useSignalsSettings = () => {
145
+ const client = (0, index_1.useClient)();
146
+ const load = React.useCallback(() => client.getSignalsSettings(), [client]);
147
+ return (0, api_hooks_1.useOvokRequest)(load);
148
+ };
149
+ exports.useSignalsSettings = useSignalsSettings;
150
+ const useUpdateSignalsSettings = () => (0, api_hooks_1.useOvokMutation)((client, params) => client.updateSignalsSettings(params.patch, params.reason));
151
+ exports.useUpdateSignalsSettings = useUpdateSignalsSettings;
152
+ const toError = (error) => error instanceof Error ? error : new Error(String(error));
package/dist/index.d.ts CHANGED
@@ -941,6 +941,7 @@ export * from "./client/carehub/types";
941
941
  export * from "./client/experience/types";
942
942
  export * from "./client/patient/types";
943
943
  export * from "./client/patient-sharing";
944
+ export { SignalsError, isSignalsError, mapSignalsError } from "./client/signals/errors";
944
945
  export * from "./client/storage";
945
946
  export * from "./client/questionnaire-response/methods";
946
947
  export * from "./client/questionnaire-response/types";
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.saveMeasurement = exports.MAX_OBSERVATION_BUNDLE_BYTES = exports.MAX_CONDITIONAL_OBSERVATION_ENTRIES = exports.OvokClient = exports.Medplum = void 0;
39
+ exports.mapSignalsError = exports.isSignalsError = exports.SignalsError = exports.saveMeasurement = exports.MAX_OBSERVATION_BUNDLE_BYTES = exports.MAX_CONDITIONAL_OBSERVATION_ENTRIES = exports.OvokClient = exports.Medplum = void 0;
40
40
  const MedplumCore = __importStar(require("@medplum/core"));
41
41
  __exportStar(require("./types"), exports);
42
42
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -64,6 +64,10 @@ __exportStar(require("./client/carehub/types"), exports);
64
64
  __exportStar(require("./client/experience/types"), exports);
65
65
  __exportStar(require("./client/patient/types"), exports);
66
66
  __exportStar(require("./client/patient-sharing"), exports);
67
+ var errors_1 = require("./client/signals/errors");
68
+ Object.defineProperty(exports, "SignalsError", { enumerable: true, get: function () { return errors_1.SignalsError; } });
69
+ Object.defineProperty(exports, "isSignalsError", { enumerable: true, get: function () { return errors_1.isSignalsError; } });
70
+ Object.defineProperty(exports, "mapSignalsError", { enumerable: true, get: function () { return errors_1.mapSignalsError; } });
67
71
  __exportStar(require("./client/storage"), exports);
68
72
  __exportStar(require("./client/questionnaire-response/methods"), exports);
69
73
  __exportStar(require("./client/questionnaire-response/types"), exports);
@@ -18,5 +18,6 @@ export * from "../client/chat/ai/types/CreateAiSessionProps";
18
18
  export * from "../client/chat/ai/types/SessionMessagesResponse";
19
19
  export * from "../client/chat/ai/types/CreateAiSessionResponse";
20
20
  export * from "../client/ai-fhir/types";
21
+ export * from "../client/signals/types";
21
22
  export * from "./ValidationError";
22
23
  export * from "@medplum/core";
@@ -38,6 +38,7 @@ __exportStar(require("../client/chat/ai/types/CreateAiSessionProps"), exports);
38
38
  __exportStar(require("../client/chat/ai/types/SessionMessagesResponse"), exports);
39
39
  __exportStar(require("../client/chat/ai/types/CreateAiSessionResponse"), exports);
40
40
  __exportStar(require("../client/ai-fhir/types"), exports);
41
+ __exportStar(require("../client/signals/types"), exports);
41
42
  // Common types
42
43
  __exportStar(require("./ValidationError"), exports);
43
44
  __exportStar(require("@medplum/core"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ovok/core",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,6 +37,7 @@
37
37
  "@types/jest": "^29.5.14",
38
38
  "@types/node": "^20.11.0",
39
39
  "@types/react": "^18.3.12",
40
+ "@types/react-test-renderer": "^18.3.1",
40
41
  "eslint": "^9.15.0",
41
42
  "eslint-plugin-check-file": "^2.8.0",
42
43
  "eslint-plugin-import": "^2.31.0",
@@ -45,6 +46,7 @@
45
46
  "globals": "^15.12.0",
46
47
  "jest": "^29.7.0",
47
48
  "react": "^18.3.1",
49
+ "react-test-renderer": "^18.3.1",
48
50
  "release-it": "^19.0.6",
49
51
  "ts-jest": "^29.2.5",
50
52
  "ts-node": "^10.9.2",
@@ -79,7 +81,9 @@
79
81
  },
80
82
  "plugins": {
81
83
  "@release-it/conventional-changelog": {
82
- "preset": "angular"
84
+ "preset": "angular",
85
+ "infile": "CHANGELOG.md",
86
+ "header": "# Changelog"
83
87
  }
84
88
  }
85
89
  }