@healthcloudai/hc-import-vitals 0.1.1

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 ADDED
@@ -0,0 +1,229 @@
1
+ # Healthcheck Import Vitals Connector
2
+
3
+ SDK connector for importing vital signs and retrieving stored vitals from the Healthcheck platform.
4
+
5
+ `HCImportVitalsClient` uses a configured and authenticated `HCLoginClient` for environment resolution and auth headers. The base URL is derived from the login client so it stays in sync with the configured environment.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ ```sh
12
+ npm install @healthcloudai/hc-import-vitals \
13
+ @healthcloudai/hc-login-connector \
14
+ @healthcloudai/hc-http
15
+ ```
16
+
17
+ ---
18
+
19
+ ## Import
20
+
21
+ ```ts
22
+ import { HCImportVitalsClient } from "@healthcloudai/hc-import-vitals";
23
+ import { HCLoginClient } from "@healthcloudai/hc-login-connector";
24
+ import { FetchClient } from "@healthcloudai/hc-http";
25
+
26
+ import type {
27
+ APIResponse,
28
+ ImportVitalsRequest,
29
+ ImportVitalDBRecord,
30
+ LogVitalsRequest,
31
+ } from "@healthcloudai/hc-import-vitals";
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Setup
37
+
38
+ ```ts
39
+ const httpClient = new FetchClient();
40
+ const loginClient = new HCLoginClient(httpClient);
41
+
42
+ loginClient.configure("healthcheck", "dev");
43
+ await loginClient.login("john.smith@example.com", "ExamplePassword123!");
44
+
45
+ const client = new HCImportVitalsClient(httpClient, loginClient);
46
+ ```
47
+
48
+ The constructor reads the base URL directly from `loginClient.getBaseUrl()` — no separate environment mapping is needed.
49
+
50
+ ---
51
+
52
+ ## API Key
53
+
54
+ If the environment requires an API key, configure it once:
55
+
56
+ ```ts
57
+ client.setApiKey("x-api-key", process.env.HEALTHCLOUD_API_KEY ?? "");
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Response Format
63
+
64
+ All methods return:
65
+
66
+ ```ts
67
+ APIResponse<T>
68
+ ```
69
+
70
+ ```ts
71
+ export interface APIResponse<T> {
72
+ Data: T | null;
73
+ IsOK: boolean;
74
+ ErrorMessage: string | null;
75
+ }
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Methods
81
+
82
+ ### `getVitals(limit?)`
83
+
84
+ Returns stored vital sign records for the authenticated patient.
85
+
86
+ - `limit` — optional, default `50`, max `500`
87
+
88
+ ```ts
89
+ await client.getVitals();
90
+ await client.getVitals(5);
91
+ ```
92
+
93
+ Returns `Promise<APIResponse<ImportVitalDBRecord[]>>`.
94
+
95
+ Example response:
96
+
97
+ ```json
98
+ {
99
+ "Data": [
100
+ {
101
+ "ID": "EC1FCB0C-26EF-45C6-B1B7-ECD1EDCE4E55",
102
+ "TenantID": "healthcheck",
103
+ "PatientID": "john.smith@example.com",
104
+ "Type": "bloodpressuresystolic",
105
+ "Value": 125,
106
+ "Unit": "mmHg",
107
+ "TimeStamp": "2026-05-27T19:46:17Z",
108
+ "SourceName": "Apple Health (Native)",
109
+ "SourcePlatform": "ios"
110
+ },
111
+ {
112
+ "ID": "93BD0AD4-C71B-4CB6-9A6F-A7E62309D28A",
113
+ "TenantID": "healthcheck",
114
+ "PatientID": "john.smith@example.com",
115
+ "Type": "bloodpressurediastolic",
116
+ "Value": 85,
117
+ "Unit": "mmHg",
118
+ "TimeStamp": "2026-05-27T19:46:17Z",
119
+ "SourceName": "Apple Health (Native)",
120
+ "SourcePlatform": "ios"
121
+ }
122
+ ],
123
+ "ErrorMessage": null,
124
+ "IsOK": true
125
+ }
126
+ ```
127
+
128
+ ---
129
+
130
+ ### `importVitals(vitals)`
131
+
132
+ Imports vital sign records from a connected device. `User.Email` and at least one record are required.
133
+
134
+ ```ts
135
+ await client.importVitals({
136
+ User: {
137
+ Email: "john.smith@example.com",
138
+ },
139
+ Records: [
140
+ {
141
+ Type: "HeartRate",
142
+ Value: 72,
143
+ Unit: "bpm",
144
+ StartDate: "2026-05-29T08:00:00.000Z",
145
+ EndDate: "2026-05-29T08:00:00.000Z",
146
+ SourceName: "Apple Health",
147
+ SourcePlatform: "ios",
148
+ },
149
+ {
150
+ Type: "BloodPressureSystolic",
151
+ Value: 120,
152
+ Unit: "mmHg",
153
+ StartDate: "2026-05-29T08:00:00.000Z",
154
+ EndDate: "2026-05-29T08:00:00.000Z",
155
+ SourceName: "Apple Health",
156
+ SourcePlatform: "ios",
157
+ },
158
+ {
159
+ Type: "BloodPressureDiastolic",
160
+ Value: 78,
161
+ Unit: "mmHg",
162
+ StartDate: "2026-05-29T08:00:00.000Z",
163
+ EndDate: "2026-05-29T08:00:00.000Z",
164
+ SourceName: "Apple Health",
165
+ SourcePlatform: "ios",
166
+ },
167
+ ],
168
+ SyncTimestamp: "2026-05-29T08:00:00.000Z",
169
+ DevicePlatform: "ios",
170
+ });
171
+ ```
172
+
173
+ Returns `Promise<APIResponse<boolean>>`.
174
+
175
+ Example response:
176
+
177
+ ```json
178
+ {
179
+ "Data": true,
180
+ "ErrorMessage": null,
181
+ "IsOK": true
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ### `logVitals(logData)`
188
+
189
+ Logs a vitals event. No authorization required — accepts any key-value data.
190
+
191
+ ```ts
192
+ await client.logVitals({
193
+ event: "sync-complete",
194
+ source: "mobile-app",
195
+ timestamp: new Date().toISOString(),
196
+ email: "john.smith@example.com",
197
+ });
198
+ ```
199
+
200
+ Returns `Promise<APIResponse<boolean>>`.
201
+
202
+ Example response:
203
+
204
+ ```json
205
+ {
206
+ "Data": true,
207
+ "ErrorMessage": null,
208
+ "IsOK": true
209
+ }
210
+ ```
211
+
212
+ ---
213
+
214
+ ## Method Summary
215
+
216
+ | Method | HTTP | Auth required |
217
+ | --- | --- | --- |
218
+ | `getVitals(limit?)` | GET `/api/imports/vitals` | Yes |
219
+ | `importVitals(vitals)` | POST `/api/imports/vitals` | Yes |
220
+ | `logVitals(logData)` | POST `/api/imports/logs` | No |
221
+
222
+ ---
223
+
224
+ ## Notes
225
+
226
+ - `getVitals` and `importVitals` require an authenticated patient token.
227
+ - `logVitals` does not require authorization.
228
+ - `Type` values observed from live data: `bloodpressuresystolic`, `bloodpressurediastolic`, `HeartRate` — always send the type exactly as the source device provides it.
229
+ - `SourcePlatform` observed values: `ios`, `manual`.
package/dist/index.cjs ADDED
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ APIError: () => import_hc_http2.APIError,
24
+ ConfigError: () => import_hc_http2.ConfigError,
25
+ HCImportVitalsClient: () => HCImportVitalsClient,
26
+ HCServiceError: () => import_hc_http2.HCServiceError,
27
+ NetworkError: () => import_hc_http2.NetworkError,
28
+ ValidationError: () => import_hc_http2.ValidationError,
29
+ errorFromHttpStatus: () => import_hc_http2.errorFromHttpStatus
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/client.ts
34
+ var import_hc_http = require("@healthcloudai/hc-http");
35
+ var HCImportVitalsClient = class {
36
+ constructor(httpClient, loginClient) {
37
+ this.http = httpClient;
38
+ this.auth = loginClient;
39
+ this.baseUrl = loginClient.getBaseUrl();
40
+ }
41
+ setApiKey(headerName, value) {
42
+ const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
43
+ const trimmedValue = value == null ? void 0 : value.trim();
44
+ if (!trimmedHeaderName) {
45
+ throw new import_hc_http.ConfigError(
46
+ "API key header name is required."
47
+ );
48
+ }
49
+ if (!trimmedValue) {
50
+ throw new import_hc_http.ConfigError(
51
+ "API key value is required."
52
+ );
53
+ }
54
+ this.apiKeyHeaderName = trimmedHeaderName;
55
+ this.apiKeyValue = trimmedValue;
56
+ }
57
+ // =========================================================================
58
+ // Import Vitals
59
+ // =========================================================================
60
+ /**
61
+ * Import vital signs from a connected device.
62
+ * Requires PatientAuthorization.
63
+ *
64
+ * @param vitals The vital signs import request
65
+ * @returns Success indicator
66
+ */
67
+ async importVitals(vitals) {
68
+ var _a, _b;
69
+ if (!vitals) {
70
+ throw new import_hc_http.ValidationError({
71
+ message: "Vitals import request is required.",
72
+ code: "INVALID_INPUT"
73
+ });
74
+ }
75
+ if (!((_b = (_a = vitals == null ? void 0 : vitals.User) == null ? void 0 : _a.Email) == null ? void 0 : _b.trim())) {
76
+ throw new import_hc_http.ValidationError({
77
+ message: "User email is required.",
78
+ code: "INVALID_INPUT"
79
+ });
80
+ }
81
+ if (!(vitals == null ? void 0 : vitals.Records) || vitals.Records.length === 0) {
82
+ throw new import_hc_http.ValidationError({
83
+ message: "At least one vital record is required.",
84
+ code: "INVALID_INPUT"
85
+ });
86
+ }
87
+ return this.execute(
88
+ "importVitals",
89
+ () => this.http.post(
90
+ `${this.baseUrl}/api/imports/vitals`,
91
+ vitals,
92
+ this.headers()
93
+ )
94
+ );
95
+ }
96
+ /**
97
+ * Get vital signs for current patient.
98
+ * Requires PatientAuthorization.
99
+ *
100
+ * @param limit Maximum number of records to retrieve (default: 50, max: 500)
101
+ * @returns List of vital sign records
102
+ */
103
+ async getVitals(limit) {
104
+ const queryLimit = limit != null ? limit : 50;
105
+ if (queryLimit <= 0) {
106
+ throw new import_hc_http.ValidationError({
107
+ message: "Limit must be greater than 0.",
108
+ code: "INVALID_INPUT"
109
+ });
110
+ }
111
+ if (queryLimit > 500) {
112
+ throw new import_hc_http.ValidationError({
113
+ message: "Limit cannot exceed 500.",
114
+ code: "INVALID_INPUT"
115
+ });
116
+ }
117
+ const qs = new URLSearchParams({ limit: queryLimit.toString() });
118
+ return this.execute(
119
+ "getVitals",
120
+ () => this.http.get(
121
+ `${this.baseUrl}/api/imports/vitals?${qs.toString()}`,
122
+ this.headers()
123
+ )
124
+ );
125
+ }
126
+ // =========================================================================
127
+ // Log Vitals
128
+ // =========================================================================
129
+ /**
130
+ * Log vital signs data.
131
+ * No authorization required.
132
+ *
133
+ * @param logData Log entry data
134
+ * @returns Success indicator
135
+ */
136
+ async logVitals(logData) {
137
+ if (!logData) {
138
+ throw new import_hc_http.ValidationError({
139
+ message: "Log data is required.",
140
+ code: "INVALID_INPUT"
141
+ });
142
+ }
143
+ return this.execute(
144
+ "logVitals",
145
+ () => this.http.post(
146
+ `${this.baseUrl}/api/imports/logs`,
147
+ logData,
148
+ this.headers()
149
+ )
150
+ );
151
+ }
152
+ // =========================================================================
153
+ // Private
154
+ // =========================================================================
155
+ /**
156
+ * Unified request execution layer.
157
+ * Handles error mapping, response validation, and error transformation.
158
+ */
159
+ async execute(operation, request) {
160
+ let response;
161
+ try {
162
+ response = await request();
163
+ } catch (err) {
164
+ if (err instanceof import_hc_http.APIError) {
165
+ throw err;
166
+ }
167
+ if (err instanceof Error) {
168
+ throw new import_hc_http.APIError({
169
+ message: `${operation}: ${err.message}`,
170
+ code: "UNKNOWN_ERROR",
171
+ details: err
172
+ });
173
+ }
174
+ throw new import_hc_http.APIError({
175
+ message: `${operation}: unexpected runtime failure`,
176
+ code: "UNKNOWN_ERROR",
177
+ details: err
178
+ });
179
+ }
180
+ if (response == null) {
181
+ throw new import_hc_http.APIError({
182
+ message: `${operation}: empty response received`,
183
+ code: "EMPTY_RESPONSE",
184
+ details: response
185
+ });
186
+ }
187
+ return response;
188
+ }
189
+ /**
190
+ * Extension point for connector-specific backend error handling.
191
+ */
192
+ mapBackendError(operation, response) {
193
+ return new import_hc_http.HCServiceError(
194
+ operation,
195
+ response.ErrorMessage || "Unknown error",
196
+ response
197
+ );
198
+ }
199
+ /**
200
+ * Validates response structure against APIResponse contract.
201
+ * Ensures all required fields are present and correctly typed.
202
+ */
203
+ isApiResponse(value) {
204
+ if (!value || typeof value !== "object") {
205
+ return false;
206
+ }
207
+ const response = value;
208
+ return "IsOK" in response && typeof response.IsOK === "boolean" && "Data" in response && "ErrorMessage" in response;
209
+ }
210
+ /**
211
+ * Builds authorization headers from login client.
212
+ * Includes standard HTTP headers for API communication.
213
+ */
214
+ headers() {
215
+ return {
216
+ Accept: "application/json",
217
+ "Content-Type": "application/json",
218
+ ...this.auth.getAuthHeader(),
219
+ ...this.getApiKeyHeader()
220
+ };
221
+ }
222
+ getApiKeyHeader() {
223
+ if (!this.apiKeyHeaderName || !this.apiKeyValue) {
224
+ return {};
225
+ }
226
+ return {
227
+ [this.apiKeyHeaderName]: this.apiKeyValue
228
+ };
229
+ }
230
+ /**
231
+ * Validates and encodes path parameter values.
232
+ * Ensures parameter is non-empty before URL encoding.
233
+ * Throws ValidationError if parameter is missing or empty.
234
+ */
235
+ encode(value) {
236
+ const trimmed = value == null ? void 0 : value.trim();
237
+ if (!trimmed) {
238
+ throw new import_hc_http.ValidationError({
239
+ message: "Path parameter value is required.",
240
+ code: "INVALID_INPUT"
241
+ });
242
+ }
243
+ return encodeURIComponent(trimmed);
244
+ }
245
+ };
246
+
247
+ // src/errors.ts
248
+ var import_hc_http2 = require("@healthcloudai/hc-http");
249
+ // Annotate the CommonJS export names for ESM import in node:
250
+ 0 && (module.exports = {
251
+ APIError,
252
+ ConfigError,
253
+ HCImportVitalsClient,
254
+ HCServiceError,
255
+ NetworkError,
256
+ ValidationError,
257
+ errorFromHttpStatus
258
+ });
@@ -0,0 +1,115 @@
1
+ import { HCLoginClient } from '@healthcloudai/hc-login-connector';
2
+ import { HttpClient, APIError } from '@healthcloudai/hc-http';
3
+ export { APIError, ConfigError, HCServiceError, NetworkError, ValidationError, errorFromHttpStatus } from '@healthcloudai/hc-http';
4
+
5
+ type ISODateString = string;
6
+ interface APIResponse<T> {
7
+ Data: T | null;
8
+ IsOK: boolean;
9
+ ErrorMessage: string | null;
10
+ }
11
+ interface APIRequest<T> {
12
+ Data: T;
13
+ }
14
+ interface ImportVitalsUser {
15
+ TenantID?: string | null;
16
+ Email?: string | null;
17
+ }
18
+ interface ImportVitalRecord {
19
+ Id?: string | null;
20
+ Type?: string | null;
21
+ Value?: number | null;
22
+ Unit?: string | null;
23
+ StartDate?: ISODateString | null;
24
+ EndDate?: ISODateString | null;
25
+ SourceName?: string | null;
26
+ SourcePlatform?: string | null;
27
+ }
28
+ interface ImportVitalsRequest {
29
+ User?: ImportVitalsUser | null;
30
+ Records?: ImportVitalRecord[] | null;
31
+ SyncTimestamp?: ISODateString | null;
32
+ DevicePlatform?: string | null;
33
+ AuthCode?: string | null;
34
+ RedirectURL?: string | null;
35
+ }
36
+ interface ImportVitalDBRecord {
37
+ ID?: string | null;
38
+ TenantID?: string | null;
39
+ PatientID?: string | null;
40
+ Type?: string | null;
41
+ Value?: number | null;
42
+ Unit?: string | null;
43
+ TimeStamp?: ISODateString | null;
44
+ SourceName?: string | null;
45
+ SourcePlatform?: string | null;
46
+ }
47
+ interface GetVitalsResponse {
48
+ vitals: ImportVitalDBRecord[];
49
+ totalCount: number;
50
+ }
51
+ interface LogVitalsRequest {
52
+ [key: string]: any;
53
+ }
54
+
55
+ declare class HCImportVitalsClient {
56
+ private readonly http;
57
+ private readonly auth;
58
+ private readonly baseUrl;
59
+ private apiKeyHeaderName?;
60
+ private apiKeyValue?;
61
+ constructor(httpClient: HttpClient, loginClient: HCLoginClient);
62
+ setApiKey(headerName: string, value: string): void;
63
+ /**
64
+ * Import vital signs from a connected device.
65
+ * Requires PatientAuthorization.
66
+ *
67
+ * @param vitals The vital signs import request
68
+ * @returns Success indicator
69
+ */
70
+ importVitals(vitals: ImportVitalsRequest): Promise<APIResponse<boolean>>;
71
+ /**
72
+ * Get vital signs for current patient.
73
+ * Requires PatientAuthorization.
74
+ *
75
+ * @param limit Maximum number of records to retrieve (default: 50, max: 500)
76
+ * @returns List of vital sign records
77
+ */
78
+ getVitals(limit?: number): Promise<APIResponse<ImportVitalDBRecord[]>>;
79
+ /**
80
+ * Log vital signs data.
81
+ * No authorization required.
82
+ *
83
+ * @param logData Log entry data
84
+ * @returns Success indicator
85
+ */
86
+ logVitals(logData: LogVitalsRequest): Promise<APIResponse<boolean>>;
87
+ /**
88
+ * Unified request execution layer.
89
+ * Handles error mapping, response validation, and error transformation.
90
+ */
91
+ protected execute<T>(operation: string, request: () => Promise<APIResponse<T>>): Promise<APIResponse<T>>;
92
+ /**
93
+ * Extension point for connector-specific backend error handling.
94
+ */
95
+ protected mapBackendError(operation: string, response: APIResponse<unknown>): APIError;
96
+ /**
97
+ * Validates response structure against APIResponse contract.
98
+ * Ensures all required fields are present and correctly typed.
99
+ */
100
+ private isApiResponse;
101
+ /**
102
+ * Builds authorization headers from login client.
103
+ * Includes standard HTTP headers for API communication.
104
+ */
105
+ private headers;
106
+ private getApiKeyHeader;
107
+ /**
108
+ * Validates and encodes path parameter values.
109
+ * Ensures parameter is non-empty before URL encoding.
110
+ * Throws ValidationError if parameter is missing or empty.
111
+ */
112
+ private encode;
113
+ }
114
+
115
+ export { type APIRequest, type APIResponse, type GetVitalsResponse, HCImportVitalsClient, type ISODateString, type ImportVitalDBRecord, type ImportVitalRecord, type ImportVitalsRequest, type ImportVitalsUser, type LogVitalsRequest };
@@ -0,0 +1,115 @@
1
+ import { HCLoginClient } from '@healthcloudai/hc-login-connector';
2
+ import { HttpClient, APIError } from '@healthcloudai/hc-http';
3
+ export { APIError, ConfigError, HCServiceError, NetworkError, ValidationError, errorFromHttpStatus } from '@healthcloudai/hc-http';
4
+
5
+ type ISODateString = string;
6
+ interface APIResponse<T> {
7
+ Data: T | null;
8
+ IsOK: boolean;
9
+ ErrorMessage: string | null;
10
+ }
11
+ interface APIRequest<T> {
12
+ Data: T;
13
+ }
14
+ interface ImportVitalsUser {
15
+ TenantID?: string | null;
16
+ Email?: string | null;
17
+ }
18
+ interface ImportVitalRecord {
19
+ Id?: string | null;
20
+ Type?: string | null;
21
+ Value?: number | null;
22
+ Unit?: string | null;
23
+ StartDate?: ISODateString | null;
24
+ EndDate?: ISODateString | null;
25
+ SourceName?: string | null;
26
+ SourcePlatform?: string | null;
27
+ }
28
+ interface ImportVitalsRequest {
29
+ User?: ImportVitalsUser | null;
30
+ Records?: ImportVitalRecord[] | null;
31
+ SyncTimestamp?: ISODateString | null;
32
+ DevicePlatform?: string | null;
33
+ AuthCode?: string | null;
34
+ RedirectURL?: string | null;
35
+ }
36
+ interface ImportVitalDBRecord {
37
+ ID?: string | null;
38
+ TenantID?: string | null;
39
+ PatientID?: string | null;
40
+ Type?: string | null;
41
+ Value?: number | null;
42
+ Unit?: string | null;
43
+ TimeStamp?: ISODateString | null;
44
+ SourceName?: string | null;
45
+ SourcePlatform?: string | null;
46
+ }
47
+ interface GetVitalsResponse {
48
+ vitals: ImportVitalDBRecord[];
49
+ totalCount: number;
50
+ }
51
+ interface LogVitalsRequest {
52
+ [key: string]: any;
53
+ }
54
+
55
+ declare class HCImportVitalsClient {
56
+ private readonly http;
57
+ private readonly auth;
58
+ private readonly baseUrl;
59
+ private apiKeyHeaderName?;
60
+ private apiKeyValue?;
61
+ constructor(httpClient: HttpClient, loginClient: HCLoginClient);
62
+ setApiKey(headerName: string, value: string): void;
63
+ /**
64
+ * Import vital signs from a connected device.
65
+ * Requires PatientAuthorization.
66
+ *
67
+ * @param vitals The vital signs import request
68
+ * @returns Success indicator
69
+ */
70
+ importVitals(vitals: ImportVitalsRequest): Promise<APIResponse<boolean>>;
71
+ /**
72
+ * Get vital signs for current patient.
73
+ * Requires PatientAuthorization.
74
+ *
75
+ * @param limit Maximum number of records to retrieve (default: 50, max: 500)
76
+ * @returns List of vital sign records
77
+ */
78
+ getVitals(limit?: number): Promise<APIResponse<ImportVitalDBRecord[]>>;
79
+ /**
80
+ * Log vital signs data.
81
+ * No authorization required.
82
+ *
83
+ * @param logData Log entry data
84
+ * @returns Success indicator
85
+ */
86
+ logVitals(logData: LogVitalsRequest): Promise<APIResponse<boolean>>;
87
+ /**
88
+ * Unified request execution layer.
89
+ * Handles error mapping, response validation, and error transformation.
90
+ */
91
+ protected execute<T>(operation: string, request: () => Promise<APIResponse<T>>): Promise<APIResponse<T>>;
92
+ /**
93
+ * Extension point for connector-specific backend error handling.
94
+ */
95
+ protected mapBackendError(operation: string, response: APIResponse<unknown>): APIError;
96
+ /**
97
+ * Validates response structure against APIResponse contract.
98
+ * Ensures all required fields are present and correctly typed.
99
+ */
100
+ private isApiResponse;
101
+ /**
102
+ * Builds authorization headers from login client.
103
+ * Includes standard HTTP headers for API communication.
104
+ */
105
+ private headers;
106
+ private getApiKeyHeader;
107
+ /**
108
+ * Validates and encodes path parameter values.
109
+ * Ensures parameter is non-empty before URL encoding.
110
+ * Throws ValidationError if parameter is missing or empty.
111
+ */
112
+ private encode;
113
+ }
114
+
115
+ export { type APIRequest, type APIResponse, type GetVitalsResponse, HCImportVitalsClient, type ISODateString, type ImportVitalDBRecord, type ImportVitalRecord, type ImportVitalsRequest, type ImportVitalsUser, type LogVitalsRequest };
package/dist/index.js ADDED
@@ -0,0 +1,237 @@
1
+ // src/client.ts
2
+ import {
3
+ APIError,
4
+ HCServiceError,
5
+ ValidationError,
6
+ ConfigError
7
+ } from "@healthcloudai/hc-http";
8
+ var HCImportVitalsClient = class {
9
+ constructor(httpClient, loginClient) {
10
+ this.http = httpClient;
11
+ this.auth = loginClient;
12
+ this.baseUrl = loginClient.getBaseUrl();
13
+ }
14
+ setApiKey(headerName, value) {
15
+ const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
16
+ const trimmedValue = value == null ? void 0 : value.trim();
17
+ if (!trimmedHeaderName) {
18
+ throw new ConfigError(
19
+ "API key header name is required."
20
+ );
21
+ }
22
+ if (!trimmedValue) {
23
+ throw new ConfigError(
24
+ "API key value is required."
25
+ );
26
+ }
27
+ this.apiKeyHeaderName = trimmedHeaderName;
28
+ this.apiKeyValue = trimmedValue;
29
+ }
30
+ // =========================================================================
31
+ // Import Vitals
32
+ // =========================================================================
33
+ /**
34
+ * Import vital signs from a connected device.
35
+ * Requires PatientAuthorization.
36
+ *
37
+ * @param vitals The vital signs import request
38
+ * @returns Success indicator
39
+ */
40
+ async importVitals(vitals) {
41
+ var _a, _b;
42
+ if (!vitals) {
43
+ throw new ValidationError({
44
+ message: "Vitals import request is required.",
45
+ code: "INVALID_INPUT"
46
+ });
47
+ }
48
+ if (!((_b = (_a = vitals == null ? void 0 : vitals.User) == null ? void 0 : _a.Email) == null ? void 0 : _b.trim())) {
49
+ throw new ValidationError({
50
+ message: "User email is required.",
51
+ code: "INVALID_INPUT"
52
+ });
53
+ }
54
+ if (!(vitals == null ? void 0 : vitals.Records) || vitals.Records.length === 0) {
55
+ throw new ValidationError({
56
+ message: "At least one vital record is required.",
57
+ code: "INVALID_INPUT"
58
+ });
59
+ }
60
+ return this.execute(
61
+ "importVitals",
62
+ () => this.http.post(
63
+ `${this.baseUrl}/api/imports/vitals`,
64
+ vitals,
65
+ this.headers()
66
+ )
67
+ );
68
+ }
69
+ /**
70
+ * Get vital signs for current patient.
71
+ * Requires PatientAuthorization.
72
+ *
73
+ * @param limit Maximum number of records to retrieve (default: 50, max: 500)
74
+ * @returns List of vital sign records
75
+ */
76
+ async getVitals(limit) {
77
+ const queryLimit = limit != null ? limit : 50;
78
+ if (queryLimit <= 0) {
79
+ throw new ValidationError({
80
+ message: "Limit must be greater than 0.",
81
+ code: "INVALID_INPUT"
82
+ });
83
+ }
84
+ if (queryLimit > 500) {
85
+ throw new ValidationError({
86
+ message: "Limit cannot exceed 500.",
87
+ code: "INVALID_INPUT"
88
+ });
89
+ }
90
+ const qs = new URLSearchParams({ limit: queryLimit.toString() });
91
+ return this.execute(
92
+ "getVitals",
93
+ () => this.http.get(
94
+ `${this.baseUrl}/api/imports/vitals?${qs.toString()}`,
95
+ this.headers()
96
+ )
97
+ );
98
+ }
99
+ // =========================================================================
100
+ // Log Vitals
101
+ // =========================================================================
102
+ /**
103
+ * Log vital signs data.
104
+ * No authorization required.
105
+ *
106
+ * @param logData Log entry data
107
+ * @returns Success indicator
108
+ */
109
+ async logVitals(logData) {
110
+ if (!logData) {
111
+ throw new ValidationError({
112
+ message: "Log data is required.",
113
+ code: "INVALID_INPUT"
114
+ });
115
+ }
116
+ return this.execute(
117
+ "logVitals",
118
+ () => this.http.post(
119
+ `${this.baseUrl}/api/imports/logs`,
120
+ logData,
121
+ this.headers()
122
+ )
123
+ );
124
+ }
125
+ // =========================================================================
126
+ // Private
127
+ // =========================================================================
128
+ /**
129
+ * Unified request execution layer.
130
+ * Handles error mapping, response validation, and error transformation.
131
+ */
132
+ async execute(operation, request) {
133
+ let response;
134
+ try {
135
+ response = await request();
136
+ } catch (err) {
137
+ if (err instanceof APIError) {
138
+ throw err;
139
+ }
140
+ if (err instanceof Error) {
141
+ throw new APIError({
142
+ message: `${operation}: ${err.message}`,
143
+ code: "UNKNOWN_ERROR",
144
+ details: err
145
+ });
146
+ }
147
+ throw new APIError({
148
+ message: `${operation}: unexpected runtime failure`,
149
+ code: "UNKNOWN_ERROR",
150
+ details: err
151
+ });
152
+ }
153
+ if (response == null) {
154
+ throw new APIError({
155
+ message: `${operation}: empty response received`,
156
+ code: "EMPTY_RESPONSE",
157
+ details: response
158
+ });
159
+ }
160
+ return response;
161
+ }
162
+ /**
163
+ * Extension point for connector-specific backend error handling.
164
+ */
165
+ mapBackendError(operation, response) {
166
+ return new HCServiceError(
167
+ operation,
168
+ response.ErrorMessage || "Unknown error",
169
+ response
170
+ );
171
+ }
172
+ /**
173
+ * Validates response structure against APIResponse contract.
174
+ * Ensures all required fields are present and correctly typed.
175
+ */
176
+ isApiResponse(value) {
177
+ if (!value || typeof value !== "object") {
178
+ return false;
179
+ }
180
+ const response = value;
181
+ return "IsOK" in response && typeof response.IsOK === "boolean" && "Data" in response && "ErrorMessage" in response;
182
+ }
183
+ /**
184
+ * Builds authorization headers from login client.
185
+ * Includes standard HTTP headers for API communication.
186
+ */
187
+ headers() {
188
+ return {
189
+ Accept: "application/json",
190
+ "Content-Type": "application/json",
191
+ ...this.auth.getAuthHeader(),
192
+ ...this.getApiKeyHeader()
193
+ };
194
+ }
195
+ getApiKeyHeader() {
196
+ if (!this.apiKeyHeaderName || !this.apiKeyValue) {
197
+ return {};
198
+ }
199
+ return {
200
+ [this.apiKeyHeaderName]: this.apiKeyValue
201
+ };
202
+ }
203
+ /**
204
+ * Validates and encodes path parameter values.
205
+ * Ensures parameter is non-empty before URL encoding.
206
+ * Throws ValidationError if parameter is missing or empty.
207
+ */
208
+ encode(value) {
209
+ const trimmed = value == null ? void 0 : value.trim();
210
+ if (!trimmed) {
211
+ throw new ValidationError({
212
+ message: "Path parameter value is required.",
213
+ code: "INVALID_INPUT"
214
+ });
215
+ }
216
+ return encodeURIComponent(trimmed);
217
+ }
218
+ };
219
+
220
+ // src/errors.ts
221
+ import {
222
+ APIError as APIError2,
223
+ ConfigError as ConfigError2,
224
+ HCServiceError as HCServiceError2,
225
+ NetworkError,
226
+ ValidationError as ValidationError2,
227
+ errorFromHttpStatus
228
+ } from "@healthcloudai/hc-http";
229
+ export {
230
+ APIError2 as APIError,
231
+ ConfigError2 as ConfigError,
232
+ HCImportVitalsClient,
233
+ HCServiceError2 as HCServiceError,
234
+ NetworkError,
235
+ ValidationError2 as ValidationError,
236
+ errorFromHttpStatus
237
+ };
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@healthcloudai/hc-import-vitals",
3
+ "version": "0.1.1",
4
+ "description": "Healthcheck Import Vitals connector SDK with TypeScript",
5
+ "author": "Healthcheck Systems Inc",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "health-cloud",
9
+ "import-vitals",
10
+ "vitals",
11
+ "react-native",
12
+ "typescript",
13
+ "sdk",
14
+ "fetch",
15
+ "axios"
16
+ ],
17
+ "type": "module",
18
+ "main": "dist/index.cjs",
19
+ "module": "dist/index.js",
20
+ "types": "dist/index.d.ts",
21
+ "react-native": "dist/index.js",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "require": "./dist/index.cjs"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
34
+ "dev": "tsup src/index.ts --watch",
35
+ "lint": "eslint src --ext .ts",
36
+ "prepublishOnly": "npm run build"
37
+ },
38
+ "dependencies": {
39
+ "@healthcloudai/hc-http": "^0.1.0",
40
+ "@healthcloudai/hc-login-connector": "^0.2.0",
41
+ "axios": "^1.13.4"
42
+ },
43
+ "peerDependencies": {
44
+ "react-native": ">=0.70.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "react-native": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "devDependencies": {
52
+ "@types/node": "^20.19.30",
53
+ "eslint": "^8.56.0",
54
+ "tsup": "^8.0.0",
55
+ "typescript": "^5.3.0"
56
+ },
57
+ "engines": {
58
+ "node": ">=18"
59
+ }
60
+ }