@healthcloudai/hc-safe-cdx 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -141,7 +141,7 @@ Public methods follow the documented patient workflow request shapes.
141
141
 
142
142
  ```ts
143
143
  safeCdx.getTestProfileByGTIN(gtin)
144
- safeCdx.getTestProfilesByAccount(includeRegisterTestDetails?)
144
+ safeCdx.listTestProfilesByAccount(includeRegisterTestDetails?)
145
145
  safeCdx.createUploadUrl(userTestResultId, gtin, imageType?)
146
146
  safeCdx.updateCvmlStatus(imageOfCaptureId, cvmlStatus)
147
147
  safeCdx.getCvmlResults(imageOfCaptureId)
@@ -159,7 +159,7 @@ safeCdx.finalizeTest(userTestResultId)
159
159
  ## Typical Patient Workflow
160
160
 
161
161
  ```ts
162
- const profiles = await safeCdx.getTestProfilesByAccount();
162
+ const profiles = await safeCdx.listTestProfilesByAccount();
163
163
 
164
164
  const profile = await safeCdx.getTestProfileByGTIN("850024942325");
165
165
 
@@ -211,23 +211,16 @@ The application is responsible for preserving workflow values returned between s
211
211
 
212
212
  ## Public Methods
213
213
 
214
- ### `getTestProfilesByAccount(...)`
214
+ ### `listTestProfilesByAccount(...)`
215
215
 
216
216
  Lists test profiles available to the authenticated patient account. The backend resolves the tenant from the authenticated patient context.
217
217
 
218
218
  #### Signature
219
219
 
220
220
  ```ts
221
- safeCdx.getTestProfilesByAccount(
221
+ safeCdx.listTestProfilesByAccount(
222
222
  includeRegisterTestDetails?: boolean
223
- ): Promise<APIResponse<GetTestProfilesByAccountData>>
224
- ```
225
-
226
- `GetTestProfilesByAccountData` is:
227
-
228
- ```ts
229
- type GetTestProfilesByAccountData =
230
- SafeAPIResponse<TestProfileByAccountItem[]>;
223
+ ): Promise<SafeAPIResponse<TestProfileByAccountItem[]>>
231
224
  ```
232
225
 
233
226
  #### Parameters
@@ -239,11 +232,11 @@ type GetTestProfilesByAccountData =
239
232
  #### Usage
240
233
 
241
234
  ```ts
242
- const profiles = await safeCdx.getTestProfilesByAccount();
235
+ const profiles = await safeCdx.listTestProfilesByAccount();
243
236
  ```
244
237
 
245
238
  ```ts
246
- const profiles = await safeCdx.getTestProfilesByAccount(true);
239
+ const profiles = await safeCdx.listTestProfilesByAccount(true);
247
240
  ```
248
241
 
249
242
  #### API request sent internally
@@ -1649,12 +1642,48 @@ const response = await safeCdx.finalizeTest(userTestResultId);
1649
1642
  ```
1650
1643
 
1651
1644
 
1645
+ ## Distinct API Host
1646
+
1647
+ Safe CDX calls a **different backend service** from all other connectors. The host is `api-hcs.healthcloud-services.com` (not `api-healthcheck.healthcloud-services.com`). The URL is derived from `loginClient.getEnvironment()`:
1648
+
1649
+ | Environment | Host |
1650
+ | --- | --- |
1651
+ | `dev` | `https://dev-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
1652
+ | `uat` | `https://uat-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
1653
+ | `prod` | `https://api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
1654
+
1655
+ `loginClient.getBaseUrl()` is **not used** by this connector — it points to the Healthcheck API, not SafeCDX.
1656
+
1657
+ ---
1658
+
1659
+ ## Response Type by Method
1660
+
1661
+ | Method | Returns |
1662
+ | --- | --- |
1663
+ | `getTestProfileByGTIN(gtin)` | `APIResponse<GetTestProfileByGTINData>` |
1664
+ | `listTestProfilesByAccount(...)` | `SafeAPIResponse<TestProfileByAccountItem[]>` |
1665
+ | `createUploadUrl(...)` | `APIResponse<CreateUploadUrlData>` |
1666
+ | `uploadImage(preSignedURL, image, contentType?)` | `Promise<void>` — direct S3 upload, no envelope |
1667
+ | `updateCvmlStatus(...)` | `SafeAPIResponse<UpdateCvmlStatusResponse>` |
1668
+ | `getCvmlResults(imageOfCaptureId)` | `SafeAPIResponse<GetCvmlResultsResponse>` |
1669
+ | `getPendingResults(excludeStatus?)` | `APIResponse<GetPendingResultsData>` |
1670
+ | `getLastResults(excludeStatus?)` | `APIResponse<GetLastResultsData>` |
1671
+ | `getTestHistory(excludeStatus?)` | `APIResponse<GetTestHistoryData>` |
1672
+ | `getResultDetails(userTestResultId)` | `SafeAPIResponse<GetResultDetailsResponse>` |
1673
+ | `getResultPdf(userTestResultId)` | `APIResponse<GetResultPdfData>` |
1674
+ | `getImageCaptureUrl(userTestResultId)` | `SafeAPIResponse<GetImageCaptureUrlResponse>` |
1675
+ | `resumeFlow(userTestResultId, resumed?)` | `APIResponse<ResumeFlowData>` |
1676
+ | `submitAnswers(submission)` | `APIResponse<SubmitAnswersData>` |
1677
+ | `finalizeTest(userTestResultId)` | `APIResponse<FinalizeTestData>` |
1678
+
1679
+ `uploadImage` bypasses the `HttpClient` entirely and calls the pre-signed storage URL directly using the native `fetch` API. Auth headers are **not** sent — the pre-signed URL provides its own short-lived credential.
1680
+
1681
+ ---
1682
+
1652
1683
  ## Notes
1653
1684
 
1654
1685
  - `HCSafeCDXClient` documents the authenticated patient workflow supported by the current client.
1655
1686
  - The connector does not perform login; it uses `HCLoginClient` for authenticated patient headers.
1656
- - Safe CDX uses its own service URL derived from `loginClient.getEnvironment()`.
1657
- - The connector does not use `loginClient.getBaseUrl()` for Safe CDX API routes.
1658
1687
  - POST payloads are wrapped internally as `{ Data: payload }`.
1659
1688
  - Patient tenant and identity context are supplied by the backend from the authenticated request.
1660
1689
  - API responses are returned in their endpoint-defined shape without being unwrapped or transformed by `HCSafeCDXClient`.
package/dist/index.cjs CHANGED
@@ -25,8 +25,7 @@ __export(index_exports, {
25
25
  HCSafeCDXClient: () => HCSafeCDXClient,
26
26
  HCServiceError: () => import_hc_http2.HCServiceError,
27
27
  NetworkError: () => import_hc_http2.NetworkError,
28
- ValidationError: () => import_hc_http2.ValidationError,
29
- errorFromHttpStatus: () => import_hc_http2.errorFromHttpStatus
28
+ ValidationError: () => import_hc_http2.ValidationError
30
29
  });
31
30
  module.exports = __toCommonJS(index_exports);
32
31
 
@@ -56,24 +55,12 @@ function buildUrl(baseUrl, route, query) {
56
55
  }
57
56
  return url.toString();
58
57
  }
59
- var HCSafeCDXClient = class {
58
+ var HCSafeCDXClient = class extends import_hc_http.HCBaseConnector {
60
59
  constructor(httpClient, loginClient) {
61
- this.http = httpClient;
62
- this.loginClient = loginClient;
60
+ super(httpClient);
61
+ this.auth = loginClient;
63
62
  this.baseUrl = buildSafeCdxBaseUrl(loginClient.getEnvironment());
64
63
  }
65
- setApiKey(headerName, value) {
66
- const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
67
- const trimmedValue = value == null ? void 0 : value.trim();
68
- if (!trimmedHeaderName) {
69
- throw new import_hc_http.ConfigError("API key header name is required.");
70
- }
71
- if (!trimmedValue) {
72
- throw new import_hc_http.ConfigError("API key value is required.");
73
- }
74
- this.apiKeyHeaderName = trimmedHeaderName;
75
- this.apiKeyValue = trimmedValue;
76
- }
77
64
  // -------------------------------------------------------------------------
78
65
  // Test Profiles
79
66
  // -------------------------------------------------------------------------
@@ -82,7 +69,7 @@ var HCSafeCDXClient = class {
82
69
  * Resolves the SafeCDX test profile associated with a GTIN barcode.
83
70
  */
84
71
  async getTestProfileByGTIN(gtin) {
85
- const resolvedGtin = this.requireValue(gtin, "gtin");
72
+ const resolvedGtin = this.requireValue(gtin, "GTIN");
86
73
  return this.execute(
87
74
  "getTestProfileByGTIN",
88
75
  () => this.http.get(
@@ -92,23 +79,21 @@ var HCSafeCDXClient = class {
92
79
  );
93
80
  }
94
81
  /**
95
- * POST test/profiles/by-account
96
-
97
- *
98
- * TenantId is resolved by the backend from the authenticated patient context.
99
- */
100
- async getTestProfilesByAccount(includeRegisterTestDetails = true) {
82
+ * POST test/profiles/by-account
83
+ *
84
+ * Returns test profiles configured for the authenticated patient's account.
85
+ * TenantId is resolved by the backend from the authenticated patient context.
86
+ */
87
+ async listTestProfilesByAccount(includeRegisterTestDetails = true) {
101
88
  const request = {
102
- Data: {
103
- IncludeRegisterTestDetails: includeRegisterTestDetails
104
- }
89
+ Data: { IncludeRegisterTestDetails: includeRegisterTestDetails }
105
90
  };
106
- return this.execute(
107
- "getTestProfilesByAccount",
91
+ return this.executeSafe(
92
+ "listTestProfilesByAccount",
108
93
  () => this.http.post(
109
94
  this.url("test/profiles/by-account"),
110
95
  request,
111
- this.getJsonHeaders()
96
+ this.getAuthHeaders()
112
97
  )
113
98
  );
114
99
  }
@@ -120,16 +105,14 @@ var HCSafeCDXClient = class {
120
105
  * Requests a pre-signed URL used to upload a test image.
121
106
  */
122
107
  async createUploadUrl(userTestResultId, gtin, imageType = "jpg") {
123
- const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
124
- const resolvedGtin = this.requireValue(gtin, "gtin");
125
- const resolvedImageType = this.requireValue(imageType, "imageType");
108
+ const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
109
+ const resolvedGtin = this.requireValue(gtin, "GTIN");
110
+ const resolvedImageType = this.requireValue(imageType, "Image type");
126
111
  const request = {
127
112
  Data: {
128
113
  Gtin: resolvedGtin,
129
114
  UserTestResultId: resolvedUserTestResultId,
130
- Metadata: {
131
- ImageType: resolvedImageType
132
- }
115
+ Metadata: { ImageType: resolvedImageType }
133
116
  }
134
117
  };
135
118
  return this.execute(
@@ -137,7 +120,7 @@ var HCSafeCDXClient = class {
137
120
  () => this.http.post(
138
121
  this.url("upload/url"),
139
122
  request,
140
- this.getJsonHeaders()
123
+ this.getAuthHeaders()
141
124
  )
142
125
  );
143
126
  }
@@ -145,17 +128,19 @@ var HCSafeCDXClient = class {
145
128
  * PUT preSignedURL
146
129
  * Uploads the image directly to the pre-signed storage URL.
147
130
  *
148
- * This request does not call a SafeCDX API route and does not send
149
- * authenticated Health Cloud headers.
131
+ * This request does NOT call a SafeCDX API route and does NOT send
132
+ * Health Cloud auth headers — it targets the storage provider directly.
133
+ * The pre-signed URL embeds credentials; no Authorization header is needed.
150
134
  */
151
135
  async uploadImage(preSignedURL, image, contentType = "image/jpeg") {
152
- const resolvedPreSignedURL = this.requireValue(preSignedURL, "preSignedURL");
153
- const resolvedContentType = this.requireValue(contentType, "contentType");
136
+ const resolvedPreSignedURL = this.requireValue(preSignedURL, "Pre-signed URL");
137
+ const resolvedContentType = this.requireValue(contentType, "Content type");
138
+ if (image == null) {
139
+ throw new import_hc_http.ValidationError({ message: "Image is required.", code: "INVALID_INPUT" });
140
+ }
154
141
  const response = await fetch(resolvedPreSignedURL, {
155
142
  method: "PUT",
156
- headers: {
157
- "Content-Type": resolvedContentType
158
- },
143
+ headers: { "Content-Type": resolvedContentType },
159
144
  body: image
160
145
  });
161
146
  if (!response.ok) {
@@ -170,10 +155,7 @@ var HCSafeCDXClient = class {
170
155
  code: response.status >= 500 ? "SERVER_ERROR" : "UNKNOWN_ERROR",
171
156
  statusCode: response.status,
172
157
  backendMessage: body,
173
- details: {
174
- status: response.status,
175
- body
176
- }
158
+ details: { status: response.status, body }
177
159
  });
178
160
  }
179
161
  }
@@ -182,11 +164,11 @@ var HCSafeCDXClient = class {
182
164
  // -------------------------------------------------------------------------
183
165
  /**
184
166
  * POST cvml/status
185
- * Returns the direct SafeCDX response without an outer APIResponse wrapper.
167
+ * Updates the CVML processing status for an image capture.
186
168
  */
187
169
  async updateCvmlStatus(imageOfCaptureId, cvmlStatus) {
188
- const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "imageOfCaptureId");
189
- const resolvedCvmlStatus = this.requireValue(cvmlStatus, "cvmlStatus");
170
+ const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "Image capture ID");
171
+ const resolvedCvmlStatus = this.requireValue(cvmlStatus, "CVML status");
190
172
  const request = {
191
173
  Data: {
192
174
  ImageOfCaptureId: resolvedImageOfCaptureId,
@@ -195,15 +177,15 @@ var HCSafeCDXClient = class {
195
177
  };
196
178
  return this.executeSafe(
197
179
  "updateCvmlStatus",
198
- () => this.http.post(this.url("cvml/status"), request, this.getJsonHeaders())
180
+ () => this.http.post(this.url("cvml/status"), request, this.getAuthHeaders())
199
181
  );
200
182
  }
201
183
  /**
202
184
  * GET cvml/results
203
- * Returns the direct SafeCDX response without an outer APIResponse wrapper.
185
+ * Returns the CVML analysis results for an image capture.
204
186
  */
205
187
  async getCvmlResults(imageOfCaptureId) {
206
- const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "imageOfCaptureId");
188
+ const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "Image capture ID");
207
189
  return this.executeSafe(
208
190
  "getCvmlResults",
209
191
  () => this.http.get(
@@ -215,85 +197,73 @@ var HCSafeCDXClient = class {
215
197
  // -------------------------------------------------------------------------
216
198
  // Test Results
217
199
  // -------------------------------------------------------------------------
218
- /** POST test/result/pending */
219
- async getPendingResults(excludeStatus = "Invalid,Canceled") {
200
+ /** POST test/result/pending — returns pending test results excluding the given statuses. */
201
+ async listPendingResults(excludeStatus = "Invalid,Canceled") {
220
202
  const request = {
221
- Data: {
222
- ExcludeStatus: excludeStatus
223
- }
203
+ Data: { ExcludeStatus: excludeStatus }
224
204
  };
225
- return this.execute(
226
- "getPendingResults",
227
- () => this.http.post(this.url("test/result/pending"), request, this.getJsonHeaders())
205
+ return this.executeSafe(
206
+ "listPendingResults",
207
+ () => this.http.post(this.url("test/result/pending"), request, this.getAuthHeaders())
228
208
  );
229
209
  }
230
- /** POST test/result/last */
210
+ /** POST test/result/last — returns the most recent test result. */
231
211
  async getLastResults(excludeStatus = "Invalid") {
232
212
  const request = {
233
- Data: {
234
- ExcludeStatus: excludeStatus
235
- }
213
+ Data: { ExcludeStatus: excludeStatus }
236
214
  };
237
- return this.execute(
215
+ return this.executeSafe(
238
216
  "getLastResults",
239
- () => this.http.post(this.url("test/result/last"), request, this.getJsonHeaders())
217
+ () => this.http.post(this.url("test/result/last"), request, this.getAuthHeaders())
240
218
  );
241
219
  }
242
- /** POST test/result/history */
243
- async getTestHistory(excludeStatus = "Invalid") {
220
+ /** POST test/result/history — returns all test results excluding the given statuses. */
221
+ async listTestHistory(excludeStatus = "Invalid") {
244
222
  const request = {
245
- Data: {
246
- ExcludeStatus: excludeStatus
247
- }
223
+ Data: { ExcludeStatus: excludeStatus }
248
224
  };
249
- return this.execute(
250
- "getTestHistory",
251
- () => this.http.post(this.url("test/result/history"), request, this.getJsonHeaders())
225
+ return this.executeSafe(
226
+ "listTestHistory",
227
+ () => this.http.post(this.url("test/result/history"), request, this.getAuthHeaders())
252
228
  );
253
229
  }
254
230
  /**
255
231
  * POST test/result/details
256
- * Returns the direct SafeCDX response without an outer APIResponse wrapper.
232
+ * Returns full details for a single test result.
257
233
  */
258
234
  async getResultDetails(userTestResultId) {
259
- const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
235
+ const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
260
236
  const request = {
261
- Data: {
262
- UserTestResultId: resolvedUserTestResultId
263
- }
237
+ Data: { UserTestResultId: resolvedUserTestResultId }
264
238
  };
265
239
  return this.executeSafe(
266
240
  "getResultDetails",
267
- () => this.http.post(this.url("test/result/details"), request, this.getJsonHeaders())
241
+ () => this.http.post(this.url("test/result/details"), request, this.getAuthHeaders())
268
242
  );
269
243
  }
270
- /** POST test/result/pdf */
244
+ /** POST test/result/pdf — returns the PDF report for a test result. */
271
245
  async getResultPdf(userTestResultId) {
272
- const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
246
+ const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
273
247
  const request = {
274
- Data: {
275
- UserTestResultId: resolvedUserTestResultId
276
- }
248
+ Data: { UserTestResultId: resolvedUserTestResultId }
277
249
  };
278
- return this.execute(
250
+ return this.executeSafe(
279
251
  "getResultPdf",
280
- () => this.http.post(this.url("test/result/pdf"), request, this.getJsonHeaders())
252
+ () => this.http.post(this.url("test/result/pdf"), request, this.getAuthHeaders())
281
253
  );
282
254
  }
283
255
  /**
284
256
  * POST test/result/image/capture
285
- * Returns the direct SafeCDX response without an outer APIResponse wrapper.
257
+ * Returns a URL to capture an image for the given test result.
286
258
  */
287
259
  async getImageCaptureUrl(userTestResultId) {
288
- const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
260
+ const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
289
261
  const request = {
290
- Data: {
291
- UserTestResultId: resolvedUserTestResultId
292
- }
262
+ Data: { UserTestResultId: resolvedUserTestResultId }
293
263
  };
294
264
  return this.executeSafe(
295
265
  "getImageCaptureUrl",
296
- () => this.http.post(this.url("test/result/image/capture"), request, this.getJsonHeaders())
266
+ () => this.http.post(this.url("test/result/image/capture"), request, this.getAuthHeaders())
297
267
  );
298
268
  }
299
269
  // -------------------------------------------------------------------------
@@ -301,152 +271,81 @@ var HCSafeCDXClient = class {
301
271
  // -------------------------------------------------------------------------
302
272
  /** POST test/resume */
303
273
  async resumeFlow(userTestResultId, resumed = true) {
304
- const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
274
+ const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
305
275
  const request = {
306
- Data: {
307
- UserTestResultId: resolvedUserTestResultId,
308
- Resumed: resumed
309
- }
276
+ Data: { UserTestResultId: resolvedUserTestResultId, Resumed: resumed }
310
277
  };
311
- return this.execute(
278
+ return this.executeSafe(
312
279
  "resumeFlow",
313
- () => this.http.post(this.url("test/resume"), request, this.getJsonHeaders())
280
+ () => this.http.post(this.url("test/resume"), request, this.getAuthHeaders())
314
281
  );
315
282
  }
316
283
  /** POST test/answers */
317
284
  async submitAnswers(submission) {
285
+ const resolvedId = this.requireValue(submission == null ? void 0 : submission.UserTestResultId, "User test result ID");
286
+ if (!(submission == null ? void 0 : submission.Result) || submission.Result.length === 0) {
287
+ throw new import_hc_http.ValidationError({
288
+ message: "At least one answer result is required.",
289
+ code: "INVALID_INPUT",
290
+ param: "Result"
291
+ });
292
+ }
318
293
  const request = {
319
- Data: submission
294
+ Data: { ...submission, UserTestResultId: resolvedId }
320
295
  };
321
- return this.execute(
296
+ return this.executeSafe(
322
297
  "submitAnswers",
323
- () => this.http.post(this.url("test/answers"), request, this.getJsonHeaders())
298
+ () => this.http.post(this.url("test/answers"), request, this.getAuthHeaders())
324
299
  );
325
300
  }
326
301
  /** POST test/finalize */
327
302
  async finalizeTest(userTestResultId) {
328
- const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
303
+ const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
329
304
  const request = {
330
- Data: {
331
- UserTestResultId: resolvedUserTestResultId
332
- }
305
+ Data: { UserTestResultId: resolvedUserTestResultId }
333
306
  };
334
307
  return this.execute(
335
308
  "finalizeTest",
336
- () => this.http.post(this.url("test/finalize"), request, this.getJsonHeaders())
309
+ () => this.http.post(this.url("test/finalize"), request, this.getAuthHeaders())
337
310
  );
338
311
  }
339
312
  // -------------------------------------------------------------------------
340
313
  // Private Helpers
341
314
  // -------------------------------------------------------------------------
342
- async execute(operation, request) {
343
- const response = await this.executeRaw(operation, request);
344
- if (!this.isApiResponse(response)) {
345
- throw new import_hc_http.APIError({
346
- message: `${operation}: invalid API response structure`,
347
- code: "INVALID_RESPONSE",
348
- details: response
349
- });
350
- }
351
- if (!response.IsOK) {
352
- throw new import_hc_http.HCServiceError(operation, response.ErrorMessage, response);
353
- }
354
- return response;
355
- }
356
315
  async executeSafe(operation, request) {
357
- var _a, _b;
358
- const response = await this.executeRaw(operation, request);
359
- if (!this.isSafeApiResponse(response)) {
360
- throw new import_hc_http.APIError({
361
- message: `${operation}: invalid SafeCDX response structure`,
362
- code: "INVALID_RESPONSE",
363
- details: response
364
- });
365
- }
366
- if (!response.success) {
367
- throw new import_hc_http.APIError({
368
- message: (_a = response.message) != null ? _a : `${operation}: backend returned success: false`,
369
- code: "BACKEND_FAILURE",
370
- statusCode: 200,
371
- backendMessage: (_b = response.message) != null ? _b : void 0,
372
- details: response
373
- });
374
- }
375
- return response;
376
- }
377
- async executeRaw(operation, request) {
316
+ var _a;
378
317
  let response;
379
318
  try {
380
319
  response = await request();
381
320
  } catch (err) {
382
- if (err instanceof import_hc_http.APIError) {
383
- throw err;
384
- }
321
+ if (err instanceof import_hc_http.APIError) throw err;
385
322
  if (err instanceof Error) {
386
- throw new import_hc_http.APIError({
387
- message: `${operation}: ${err.message}`,
388
- code: "UNKNOWN_ERROR",
389
- details: err
390
- });
323
+ throw new import_hc_http.APIError({ message: `${operation}: ${err.message}`, code: "UNKNOWN_ERROR", details: err });
391
324
  }
392
- throw new import_hc_http.APIError({
393
- message: `${operation}: unexpected runtime failure`,
394
- code: "UNKNOWN_ERROR",
395
- details: err
396
- });
325
+ throw new import_hc_http.APIError({ message: `${operation}: unexpected runtime failure`, code: "UNKNOWN_ERROR", details: err });
397
326
  }
398
327
  if (response == null) {
399
- throw new import_hc_http.APIError({
400
- message: `${operation}: empty response received`,
401
- code: "EMPTY_RESPONSE",
402
- details: response
403
- });
328
+ throw new import_hc_http.APIError({ message: `${operation}: empty response received`, code: "EMPTY_RESPONSE", details: response });
404
329
  }
405
- return response;
406
- }
407
- isApiResponse(value) {
408
- if (!value || typeof value !== "object") {
409
- return false;
330
+ if (!this.isSafeApiResponse(response)) {
331
+ throw new import_hc_http.APIError({ message: `${operation}: invalid SafeCDX response structure`, code: "INVALID_RESPONSE", details: response });
410
332
  }
411
- const response = value;
412
- return "IsOK" in response && typeof response.IsOK === "boolean" && "Data" in response && "ErrorMessage" in response;
413
- }
414
- isSafeApiResponse(value) {
415
- if (!value || typeof value !== "object") {
416
- return false;
333
+ if (!response.success) {
334
+ throw new import_hc_http.HCServiceError(
335
+ operation,
336
+ (_a = response.message) != null ? _a : "Backend returned success: false",
337
+ response
338
+ );
417
339
  }
418
- const response = value;
419
- return "success" in response && typeof response.success === "boolean" && "data" in response && "message" in response && "code" in response && typeof response.code === "number";
340
+ return response;
420
341
  }
421
- requireValue(value, parameterName) {
422
- const trimmedValue = value == null ? void 0 : value.trim();
423
- if (!trimmedValue) {
424
- throw new import_hc_http.ValidationError({
425
- message: `${parameterName} is required.`,
426
- code: "INVALID_INPUT"
427
- });
428
- }
429
- return trimmedValue;
342
+ isSafeApiResponse(value) {
343
+ if (!value || typeof value !== "object") return false;
344
+ const r = value;
345
+ return "success" in r && typeof r.success === "boolean" && "data" in r && "message" in r && "code" in r && typeof r.code === "number";
430
346
  }
431
347
  getAuthHeaders() {
432
- return {
433
- ...this.loginClient.getAuthHeader(),
434
- ...this.getApiKeyHeader()
435
- };
436
- }
437
- getJsonHeaders() {
438
- return {
439
- ...this.getAuthHeaders(),
440
- "Content-Type": "application/json"
441
- };
442
- }
443
- getApiKeyHeader() {
444
- if (!this.apiKeyHeaderName || !this.apiKeyValue) {
445
- return {};
446
- }
447
- return {
448
- [this.apiKeyHeaderName]: this.apiKeyValue
449
- };
348
+ return { ...this.getHeaders(), ...this.auth.getAuthHeader() };
450
349
  }
451
350
  url(route, query) {
452
351
  return buildUrl(this.baseUrl, route, query);
@@ -462,6 +361,5 @@ var import_hc_http2 = require("@healthcloudai/hc-http");
462
361
  HCSafeCDXClient,
463
362
  HCServiceError,
464
363
  NetworkError,
465
- ValidationError,
466
- errorFromHttpStatus
364
+ ValidationError
467
365
  });