@healthcloudai/hc-safe-cdx 0.1.1 → 0.1.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
@@ -2,23 +2,28 @@
2
2
 
3
3
  Safe CDX connector provides a thin TypeScript wrapper around Safe CDX API routes.
4
4
 
5
- The connector is designed to work with the shared `HttpClient` from `@healthcloudai/hc-http`, the same way as other Health Cloud connector modules. It does not implement its own transport layer for standard API calls.
5
+ The connector follows the same shared-client pattern used by other Health Cloud connector modules:
6
6
 
7
- Standard Safe CDX API calls are executed through the injected `HttpClient`.
7
+ - `HttpClient` handles HTTP transport.
8
+ - `HCLoginClient` provides the authenticated request headers.
9
+ - `HCSafeCDXClient` builds Safe CDX URLs, wraps request payloads, and exposes one method per Safe CDX route.
10
+
11
+ Safe CDX has its own API host. The connector uses `HCLoginClient` for authentication only; it does not use `loginClient.getBaseUrl()` for Safe CDX routes.
8
12
 
9
13
  Image upload is handled separately because the upload target is a pre-signed S3 URL, not a Safe CDX API route.
10
14
 
11
15
  ## Installation
12
16
 
13
17
  ```sh
14
- npm install @healthcloudai/hc-safe-cdx @healthcloudai/hc-http
18
+ npm install @healthcloudai/hc-safe-cdx @healthcloudai/hc-http @healthcloudai/hc-login-connector
15
19
  ```
16
20
 
17
21
  ## Import
18
22
 
19
23
  ```ts
20
24
  import { FetchClient } from "@healthcloudai/hc-http";
21
- import { SafeCDXClient } from "@healthcloudai/hc-safe-cdx";
25
+ import { HCLoginClient } from "@healthcloudai/hc-login-connector";
26
+ import { HCSafeCDXClient } from "@healthcloudai/hc-safe-cdx";
22
27
  ```
23
28
 
24
29
  ## Basic Setup
@@ -26,47 +31,85 @@ import { SafeCDXClient } from "@healthcloudai/hc-safe-cdx";
26
31
  ```ts
27
32
  const http = new FetchClient();
28
33
 
29
- const safeCdx = new SafeCDXClient(http, {
34
+ const loginClient = new HCLoginClient(http);
35
+
36
+ // Configure and authenticate the login client first.
37
+ // Exact login/configuration calls depend on the login connector setup used by the app.
38
+
39
+ const safeCdx = new HCSafeCDXClient(http, loginClient, {
30
40
  environment: "dev",
31
41
  defaultLanguage: "en",
32
42
  defaultImageType: "jpg",
33
43
  });
44
+ ```
45
+
46
+ The connector does not perform login. The consuming application should authenticate through the Health Cloud login connector first. Safe CDX then reuses the authenticated headers from `loginClient.getAuthHeader()`.
47
+
48
+ There is no `setAccessToken()` step in this version of the client.
49
+
50
+ ## Dependency Notes
51
+
52
+ Because `HCSafeCDXClient` accepts `HCLoginClient` in the constructor, `@healthcloudai/hc-login-connector` should be included as a package dependency.
34
53
 
35
- safeCdx.setAccessToken(patientAccessToken);
54
+ Example package configuration:
55
+
56
+ ```json
57
+ {
58
+ "dependencies": {
59
+ "@healthcloudai/hc-http": "^0.x.x",
60
+ "@healthcloudai/hc-login-connector": "^0.x.x"
61
+ }
62
+ }
36
63
  ```
37
64
 
38
- The connector requires a logged-in patient token.
65
+ In a workspace setup, use the same dependency versioning strategy already used by the other connector packages.
39
66
 
40
- The connector does not perform login. Login should be handled through the patient authentication flow first. After login, the patient token should be passed to Safe CDX by calling `setAccessToken()`.
67
+ Example:
68
+
69
+ ```json
70
+ {
71
+ "dependencies": {
72
+ "@healthcloudai/hc-http": "workspace:*",
73
+ "@healthcloudai/hc-login-connector": "workspace:*"
74
+ }
75
+ }
76
+ ```
41
77
 
42
78
  ## How the Client Works
43
79
 
44
- The client follows the same structure as the other Health Cloud connector modules:
80
+ The client receives both the HTTP client and the login client:
45
81
 
46
82
  ```ts
47
83
  const http = new FetchClient();
48
- const safeCdx = new SafeCDXClient(http, config);
84
+ const loginClient = new HCLoginClient(http);
85
+
86
+ const safeCdx = new HCSafeCDXClient(http, loginClient, {
87
+ environment: "dev",
88
+ });
49
89
  ```
50
90
 
51
91
  The shared `HttpClient` is responsible for executing HTTP requests.
52
92
 
93
+ The `HCLoginClient` is responsible for the authenticated request headers.
94
+
53
95
  The Safe CDX client is responsible for:
54
96
 
97
+ - resolving the Safe CDX environment host
55
98
  - building Safe CDX URLs
56
- - resolving the environment host
57
- - adding the authorization header
99
+ - adding auth headers from `HCLoginClient`
100
+ - adding optional API key headers when configured
58
101
  - wrapping POST request payloads into `{ Data: payload }`
59
102
  - validating `ApiResponse` results where the backend returns `{ IsOK, Data, ErrorMessage }`
60
- - exposing one SDK method per API route
103
+ - exposing one SDK method per Safe CDX route
61
104
 
62
- The Safe CDX client does not create its own internal `fetch` helpers for standard API routes.
105
+ The Safe CDX client does not create its own internal `fetch` helpers for standard Safe CDX API routes.
63
106
 
64
107
  ## Environment Configuration
65
108
 
66
109
  The connector supports the following environments:
67
110
 
68
111
  ```ts
69
- const safeCdx = new SafeCDXClient(http, {
112
+ const safeCdx = new HCSafeCDXClient(http, loginClient, {
70
113
  environment: "dev",
71
114
  });
72
115
  ```
@@ -77,35 +120,54 @@ Available environments:
77
120
  "dev" | "uat" | "prod"
78
121
  ```
79
122
 
80
- Environment host mapping is handled internally by the client.
123
+ Safe CDX environment host mapping is handled internally by the connector:
81
124
 
82
- ## Authorization
125
+ ```txt
126
+ dev -> dev-api-hcs.healthcloud-services.com
127
+ uat -> uat-api-hcs.healthcloud-services.com
128
+ prod -> api-hcs.healthcloud-services.com
129
+ ```
83
130
 
84
- Safe CDX API calls require a Bearer token.
131
+ These hosts are separate from the login connector base URL.
85
132
 
86
- ```ts
87
- safeCdx.setAccessToken(patientAccessToken);
88
- ```
133
+ ## Authentication
89
134
 
90
- The token may be passed with or without the `Bearer` prefix.
135
+ Safe CDX API calls require authenticated Health Cloud headers.
91
136
 
92
- Both examples are valid:
137
+ The connector gets those headers from:
93
138
 
94
139
  ```ts
95
- safeCdx.setAccessToken("eyJ...");
140
+ loginClient.getAuthHeader()
96
141
  ```
97
142
 
143
+ This means the login connector must be configured and authenticated before Safe CDX methods are called.
144
+
145
+ Example flow:
146
+
98
147
  ```ts
99
- safeCdx.setAccessToken("Bearer eyJ...");
148
+ const http = new FetchClient();
149
+
150
+ const loginClient = new HCLoginClient(http);
151
+
152
+ // loginClient.configure(...)
153
+ // await loginClient.loginPatient(...)
154
+
155
+ const safeCdx = new HCSafeCDXClient(http, loginClient, {
156
+ environment: "dev",
157
+ });
100
158
  ```
101
159
 
102
- The client normalizes the value internally and sends it as:
160
+ Safe CDX does not manually receive a token. It does not expose `setAccessToken()`. Auth is inherited from the configured login client.
161
+
162
+ ## Optional API Key Header
163
+
164
+ If an API key header is required by the environment, it can be set on the Safe CDX client:
103
165
 
104
166
  ```ts
105
- Authorization: Bearer <token>
167
+ safeCdx.setApiKey("x-api-key", apiKeyValue);
106
168
  ```
107
169
 
108
- If no token is set before calling an API method, the client throws a `SafeCDXError`.
170
+ When configured, the API key header is added to Safe CDX API calls together with the login auth headers.
109
171
 
110
172
  ## Request Payload Wrapping
111
173
 
@@ -115,29 +177,38 @@ The SDK wraps the payload internally as:
115
177
 
116
178
  ```ts
117
179
  {
118
- "Data": payload
180
+ Data: payload
119
181
  }
120
182
  ```
121
183
 
122
184
  Correct:
123
185
 
124
186
  ```ts
125
- await safeCdx.scan2Ddm({
126
- // scan payload
187
+ await safeCdx.submitAnswers({
188
+ UserTestResultId: userTestResultId,
189
+ Result: [
190
+ {
191
+ Analyte: "Purchase",
192
+ ReportedValue: "drug store",
193
+ StoredValue: "drug store",
194
+ Score: "0",
195
+ },
196
+ ],
127
197
  });
128
198
  ```
129
199
 
130
200
  Incorrect:
131
201
 
132
202
  ```ts
133
- await safeCdx.scan2Ddm({
203
+ await safeCdx.submitAnswers({
134
204
  Data: {
135
- // scan payload
136
- }
205
+ UserTestResultId: userTestResultId,
206
+ Result: [],
207
+ },
137
208
  });
138
209
  ```
139
210
 
140
- The caller should not manually add the `Data` wrapper unless a specific method type explicitly requires it.
211
+ The caller should not manually add the `Data` wrapper.
141
212
 
142
213
  ## API Response Handling
143
214
 
@@ -155,6 +226,8 @@ For these endpoints, the client validates `IsOK`.
155
226
 
156
227
  If `IsOK` is `false`, the client throws `SafeCDXError`.
157
228
 
229
+ This validation exists because an HTTP `200` response does not always mean the backend operation succeeded. The HTTP client validates the transport layer, while the Safe CDX client validates the API response envelope.
230
+
158
231
  Some endpoints return a raw service result instead of the standard `ApiResponse` envelope. Those methods return the raw result directly.
159
232
 
160
233
  Raw result methods include:
@@ -189,13 +262,15 @@ A typical flow looks like this:
189
262
  ```ts
190
263
  const http = new FetchClient();
191
264
 
192
- const safeCdx = new SafeCDXClient(http, {
265
+ const loginClient = new HCLoginClient(http);
266
+
267
+ // Configure and authenticate loginClient first.
268
+
269
+ const safeCdx = new HCSafeCDXClient(http, loginClient, {
193
270
  environment: "dev",
194
271
  defaultLanguage: "en",
195
272
  defaultImageType: "jpg",
196
273
  });
197
-
198
- safeCdx.setAccessToken(patientAccessToken);
199
274
  ```
200
275
 
201
276
  ### 1. Get available test profiles
@@ -344,6 +419,8 @@ const scan = await safeCdx.scan2Ddm(payload);
344
419
 
345
420
  Scans a 2D data matrix barcode and resolves the related test data.
346
421
 
422
+ This method requires a real 2D DataMatrix payload. A plain GTIN should not be used as the DataMatrix payload.
423
+
347
424
  ### getTestProfile
348
425
 
349
426
  ```ts
@@ -513,6 +590,8 @@ const result = await safeCdx.submitAnswers(payload);
513
590
 
514
591
  Submits analyte answers for a test attempt.
515
592
 
593
+ `Result` should contain at least one answer item. The connector does not generate analyte answers automatically.
594
+
516
595
  ### finalizeTest
517
596
 
518
597
  ```ts
@@ -521,38 +600,20 @@ const result = await safeCdx.finalizeTest(payload);
521
600
 
522
601
  Finalizes a test attempt with CTA, indication, and decision results.
523
602
 
524
- ## Error Handling
525
-
526
- The connector throws `SafeCDXError` for connector-level errors.
527
-
528
- Examples:
529
-
530
- - invalid environment
531
- - missing access token
532
- - failed `ApiResponse` validation
533
- - failed image upload
534
-
535
- ```ts
536
- try {
537
- const profile = await safeCdx.getTestProfileByGTIN("850024942325");
538
- } catch (error) {
539
- if (error instanceof SafeCDXError) {
540
- console.error(error.message);
541
- console.error(error.response);
542
- }
543
-
544
- throw error;
545
- }
546
- ```
603
+ The payload should include:
547
604
 
548
- HTTP-level errors from the shared `HttpClient` may also be thrown depending on the `HttpClient` implementation.
605
+ - `UserTestResultId`
606
+ - `ctaResult`
607
+ - `indicationResult`
608
+ - `decisionResult`
549
609
 
550
- For example, `FetchClient` from `@healthcloudai/hc-http` throws an HTTP error when the response status is not successful.
551
610
 
552
611
  ## Notes
553
612
 
554
613
  - The connector does not perform login.
555
- - The connector expects a valid patient token to be set before API calls.
614
+ - The connector uses `HCLoginClient` for authenticated request headers.
615
+ - The connector does not use `loginClient.getBaseUrl()` for Safe CDX API routes.
616
+ - Safe CDX routes use the Safe CDX environment host from `SafeCDXConfig`.
556
617
  - Standard Safe CDX API methods use the shared `HttpClient`.
557
618
  - POST payloads are wrapped internally as `{ Data: payload }`.
558
619
  - The caller should pass only the inner payload to SDK methods.
package/dist/index.cjs CHANGED
@@ -67,46 +67,52 @@ function assertApiResponse(envelope, route) {
67
67
  return envelope;
68
68
  }
69
69
  var HCSafeCDXClient = class {
70
- constructor(httpClient, config) {
70
+ constructor(httpClient, authClient, config) {
71
71
  var _a;
72
72
  if (!ENV_HOST[config.environment]) {
73
73
  throw new SafeCDXError(`Invalid environment: ${config.environment}`);
74
74
  }
75
75
  this.http = httpClient;
76
+ this.auth = authClient;
76
77
  this.host = ENV_HOST[config.environment];
77
78
  this.defaultLanguage = config.defaultLanguage;
78
79
  this.defaultImageType = (_a = config.defaultImageType) != null ? _a : "jpg";
79
80
  }
80
- setAccessToken(token) {
81
- const trimmedToken = token == null ? void 0 : token.trim();
82
- if (!trimmedToken) {
83
- throw new SafeCDXError("Access token is required.");
81
+ setApiKey(headerName, value) {
82
+ const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
83
+ const trimmedValue = value == null ? void 0 : value.trim();
84
+ if (!trimmedHeaderName) {
85
+ throw new SafeCDXError("API key header name is required.");
84
86
  }
85
- this.accessToken = trimmedToken;
87
+ if (!trimmedValue) {
88
+ throw new SafeCDXError("API key value is required.");
89
+ }
90
+ this.apiKeyHeaderName = trimmedHeaderName;
91
+ this.apiKeyValue = trimmedValue;
86
92
  }
87
93
  // ---------------------------------------------------------------------------
88
94
  // Private helpers
89
95
  // ---------------------------------------------------------------------------
90
- getAuthorizationHeader() {
91
- if (!this.accessToken) {
92
- throw new SafeCDXError(
93
- "SafeCDX requires an access token. Call setAccessToken() first."
94
- );
95
- }
96
- const token = this.accessToken.replace(/^Bearer\s+/i, "").trim();
97
- return `Bearer ${token}`;
98
- }
99
96
  getAuthHeaders() {
100
97
  return {
101
- Authorization: this.getAuthorizationHeader()
98
+ ...this.auth.getAuthHeader(),
99
+ ...this.getApiKeyHeader()
102
100
  };
103
101
  }
104
- getJsonAuthHeaders() {
102
+ getJsonHeaders() {
105
103
  return {
106
- Authorization: this.getAuthorizationHeader(),
104
+ ...this.getAuthHeaders(),
107
105
  "Content-Type": "application/json"
108
106
  };
109
107
  }
108
+ getApiKeyHeader() {
109
+ if (!this.apiKeyHeaderName || !this.apiKeyValue) {
110
+ return {};
111
+ }
112
+ return {
113
+ [this.apiKeyHeaderName]: this.apiKeyValue
114
+ };
115
+ }
110
116
  url(route, query) {
111
117
  return buildUrl(this.host, route, query);
112
118
  }
@@ -134,7 +140,7 @@ var HCSafeCDXClient = class {
134
140
  const envelope = await this.http.post(
135
141
  this.url("scan/2ddm"),
136
142
  wrapData(payload),
137
- this.getJsonAuthHeaders()
143
+ this.getJsonHeaders()
138
144
  );
139
145
  return assertApiResponse(envelope, "scan/2ddm");
140
146
  }
@@ -149,7 +155,7 @@ var HCSafeCDXClient = class {
149
155
  const envelope = await this.http.post(
150
156
  this.url("test/profile"),
151
157
  wrapData(payload),
152
- this.getJsonAuthHeaders()
158
+ this.getJsonHeaders()
153
159
  );
154
160
  return assertApiResponse(envelope, "test/profile");
155
161
  }
@@ -163,7 +169,7 @@ var HCSafeCDXClient = class {
163
169
  const envelope = await this.http.post(
164
170
  this.url("test/profiles/by-account"),
165
171
  wrapData(payload),
166
- this.getJsonAuthHeaders()
172
+ this.getJsonHeaders()
167
173
  );
168
174
  return assertApiResponse(envelope, "test/profiles/by-account");
169
175
  }
@@ -186,14 +192,16 @@ var HCSafeCDXClient = class {
186
192
  const envelope = await this.http.post(
187
193
  this.url("upload/url"),
188
194
  wrapData(payload),
189
- this.getJsonAuthHeaders()
195
+ this.getJsonHeaders()
190
196
  );
191
197
  return assertApiResponse(envelope, "upload/url");
192
198
  }
193
199
  /**
194
200
  * PUT preSignedURL — direct S3 upload, not a SafeCDX API route.
195
201
  * preSignedURL comes from createUploadUrl() response: Data.preSignedURL.
196
-
202
+ *
203
+ * This intentionally does not use the shared HttpClient because the existing
204
+ * HttpClient.put() is JSON-oriented and stringifies the request body.
197
205
  */
198
206
  async uploadImage(preSignedURL, image, contentType = "image/jpeg") {
199
207
  const response = await fetch(preSignedURL, {
@@ -234,7 +242,7 @@ var HCSafeCDXClient = class {
234
242
  return this.http.post(
235
243
  this.url("cvml/status"),
236
244
  wrapData(payload),
237
- this.getJsonAuthHeaders()
245
+ this.getJsonHeaders()
238
246
  );
239
247
  }
240
248
  /**
@@ -263,7 +271,7 @@ var HCSafeCDXClient = class {
263
271
  const envelope = await this.http.post(
264
272
  this.url("test/result/pending"),
265
273
  wrapData(payload),
266
- this.getJsonAuthHeaders()
274
+ this.getJsonHeaders()
267
275
  );
268
276
  return assertApiResponse(envelope, "test/result/pending");
269
277
  }
@@ -275,7 +283,7 @@ var HCSafeCDXClient = class {
275
283
  const envelope = await this.http.post(
276
284
  this.url("test/result/last"),
277
285
  wrapData(payload),
278
- this.getJsonAuthHeaders()
286
+ this.getJsonHeaders()
279
287
  );
280
288
  return assertApiResponse(envelope, "test/result/last");
281
289
  }
@@ -287,7 +295,7 @@ var HCSafeCDXClient = class {
287
295
  const envelope = await this.http.post(
288
296
  this.url("test/result/history"),
289
297
  wrapData(payload),
290
- this.getJsonAuthHeaders()
298
+ this.getJsonHeaders()
291
299
  );
292
300
  return assertApiResponse(envelope, "test/result/history");
293
301
  }
@@ -303,7 +311,7 @@ var HCSafeCDXClient = class {
303
311
  return this.http.post(
304
312
  this.url("test/result/details"),
305
313
  wrapData(payload),
306
- this.getJsonAuthHeaders()
314
+ this.getJsonHeaders()
307
315
  );
308
316
  }
309
317
  /**
@@ -314,7 +322,7 @@ var HCSafeCDXClient = class {
314
322
  const envelope = await this.http.post(
315
323
  this.url("test/result/pdf"),
316
324
  wrapData(payload),
317
- this.getJsonAuthHeaders()
325
+ this.getJsonHeaders()
318
326
  );
319
327
  return assertApiResponse(envelope, "test/result/pdf");
320
328
  }
@@ -330,7 +338,7 @@ var HCSafeCDXClient = class {
330
338
  return this.http.post(
331
339
  this.url("test/result/image/capture"),
332
340
  wrapData(payload),
333
- this.getJsonAuthHeaders()
341
+ this.getJsonHeaders()
334
342
  );
335
343
  }
336
344
  /**
@@ -341,7 +349,7 @@ var HCSafeCDXClient = class {
341
349
  const envelope = await this.http.post(
342
350
  this.url("test/result/analytics"),
343
351
  wrapData(payload),
344
- this.getJsonAuthHeaders()
352
+ this.getJsonHeaders()
345
353
  );
346
354
  return assertApiResponse(envelope, "test/result/analytics");
347
355
  }
@@ -360,7 +368,7 @@ var HCSafeCDXClient = class {
360
368
  const envelope = await this.http.post(
361
369
  this.url("test/resume"),
362
370
  wrapData(payload),
363
- this.getJsonAuthHeaders()
371
+ this.getJsonHeaders()
364
372
  );
365
373
  return assertApiResponse(envelope, "test/resume");
366
374
  }
@@ -372,7 +380,7 @@ var HCSafeCDXClient = class {
372
380
  const envelope = await this.http.post(
373
381
  this.url("test/answers"),
374
382
  wrapData(payload),
375
- this.getJsonAuthHeaders()
383
+ this.getJsonHeaders()
376
384
  );
377
385
  return assertApiResponse(envelope, "test/answers");
378
386
  }
@@ -384,7 +392,7 @@ var HCSafeCDXClient = class {
384
392
  const envelope = await this.http.post(
385
393
  this.url("test/finalize"),
386
394
  wrapData(payload),
387
- this.getJsonAuthHeaders()
395
+ this.getJsonHeaders()
388
396
  );
389
397
  return assertApiResponse(envelope, "test/finalize");
390
398
  }
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import { HCLoginClient } from '@healthcloudai/hc-login-connector';
1
2
  import { HttpClient } from '@healthcloudai/hc-http';
2
3
 
3
4
  type Environment = "dev" | "uat" | "prod";
@@ -256,15 +257,17 @@ interface FinalizeTestRequest {
256
257
 
257
258
  declare class HCSafeCDXClient {
258
259
  private readonly http;
260
+ private readonly auth;
259
261
  private readonly host;
260
- private accessToken;
261
- private defaultLanguage;
262
- private defaultImageType;
263
- constructor(httpClient: HttpClient, config: SafeCDXConfig);
264
- setAccessToken(token: string): void;
265
- private getAuthorizationHeader;
262
+ private readonly defaultLanguage;
263
+ private readonly defaultImageType;
264
+ private apiKeyHeaderName?;
265
+ private apiKeyValue?;
266
+ constructor(httpClient: HttpClient, authClient: HCLoginClient, config: SafeCDXConfig);
267
+ setApiKey(headerName: string, value: string): void;
266
268
  private getAuthHeaders;
267
- private getJsonAuthHeaders;
269
+ private getJsonHeaders;
270
+ private getApiKeyHeader;
268
271
  private url;
269
272
  /**
270
273
  * GET gs1/:gtin
@@ -295,7 +298,9 @@ declare class HCSafeCDXClient {
295
298
  /**
296
299
  * PUT preSignedURL — direct S3 upload, not a SafeCDX API route.
297
300
  * preSignedURL comes from createUploadUrl() response: Data.preSignedURL.
298
-
301
+ *
302
+ * This intentionally does not use the shared HttpClient because the existing
303
+ * HttpClient.put() is JSON-oriented and stringifies the request body.
299
304
  */
300
305
  uploadImage(preSignedURL: string, image: BodyInit, contentType?: string): Promise<void>;
301
306
  /**
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { HCLoginClient } from '@healthcloudai/hc-login-connector';
1
2
  import { HttpClient } from '@healthcloudai/hc-http';
2
3
 
3
4
  type Environment = "dev" | "uat" | "prod";
@@ -256,15 +257,17 @@ interface FinalizeTestRequest {
256
257
 
257
258
  declare class HCSafeCDXClient {
258
259
  private readonly http;
260
+ private readonly auth;
259
261
  private readonly host;
260
- private accessToken;
261
- private defaultLanguage;
262
- private defaultImageType;
263
- constructor(httpClient: HttpClient, config: SafeCDXConfig);
264
- setAccessToken(token: string): void;
265
- private getAuthorizationHeader;
262
+ private readonly defaultLanguage;
263
+ private readonly defaultImageType;
264
+ private apiKeyHeaderName?;
265
+ private apiKeyValue?;
266
+ constructor(httpClient: HttpClient, authClient: HCLoginClient, config: SafeCDXConfig);
267
+ setApiKey(headerName: string, value: string): void;
266
268
  private getAuthHeaders;
267
- private getJsonAuthHeaders;
269
+ private getJsonHeaders;
270
+ private getApiKeyHeader;
268
271
  private url;
269
272
  /**
270
273
  * GET gs1/:gtin
@@ -295,7 +298,9 @@ declare class HCSafeCDXClient {
295
298
  /**
296
299
  * PUT preSignedURL — direct S3 upload, not a SafeCDX API route.
297
300
  * preSignedURL comes from createUploadUrl() response: Data.preSignedURL.
298
-
301
+ *
302
+ * This intentionally does not use the shared HttpClient because the existing
303
+ * HttpClient.put() is JSON-oriented and stringifies the request body.
299
304
  */
300
305
  uploadImage(preSignedURL: string, image: BodyInit, contentType?: string): Promise<void>;
301
306
  /**
package/dist/index.js CHANGED
@@ -40,46 +40,52 @@ function assertApiResponse(envelope, route) {
40
40
  return envelope;
41
41
  }
42
42
  var HCSafeCDXClient = class {
43
- constructor(httpClient, config) {
43
+ constructor(httpClient, authClient, config) {
44
44
  var _a;
45
45
  if (!ENV_HOST[config.environment]) {
46
46
  throw new SafeCDXError(`Invalid environment: ${config.environment}`);
47
47
  }
48
48
  this.http = httpClient;
49
+ this.auth = authClient;
49
50
  this.host = ENV_HOST[config.environment];
50
51
  this.defaultLanguage = config.defaultLanguage;
51
52
  this.defaultImageType = (_a = config.defaultImageType) != null ? _a : "jpg";
52
53
  }
53
- setAccessToken(token) {
54
- const trimmedToken = token == null ? void 0 : token.trim();
55
- if (!trimmedToken) {
56
- throw new SafeCDXError("Access token is required.");
54
+ setApiKey(headerName, value) {
55
+ const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
56
+ const trimmedValue = value == null ? void 0 : value.trim();
57
+ if (!trimmedHeaderName) {
58
+ throw new SafeCDXError("API key header name is required.");
57
59
  }
58
- this.accessToken = trimmedToken;
60
+ if (!trimmedValue) {
61
+ throw new SafeCDXError("API key value is required.");
62
+ }
63
+ this.apiKeyHeaderName = trimmedHeaderName;
64
+ this.apiKeyValue = trimmedValue;
59
65
  }
60
66
  // ---------------------------------------------------------------------------
61
67
  // Private helpers
62
68
  // ---------------------------------------------------------------------------
63
- getAuthorizationHeader() {
64
- if (!this.accessToken) {
65
- throw new SafeCDXError(
66
- "SafeCDX requires an access token. Call setAccessToken() first."
67
- );
68
- }
69
- const token = this.accessToken.replace(/^Bearer\s+/i, "").trim();
70
- return `Bearer ${token}`;
71
- }
72
69
  getAuthHeaders() {
73
70
  return {
74
- Authorization: this.getAuthorizationHeader()
71
+ ...this.auth.getAuthHeader(),
72
+ ...this.getApiKeyHeader()
75
73
  };
76
74
  }
77
- getJsonAuthHeaders() {
75
+ getJsonHeaders() {
78
76
  return {
79
- Authorization: this.getAuthorizationHeader(),
77
+ ...this.getAuthHeaders(),
80
78
  "Content-Type": "application/json"
81
79
  };
82
80
  }
81
+ getApiKeyHeader() {
82
+ if (!this.apiKeyHeaderName || !this.apiKeyValue) {
83
+ return {};
84
+ }
85
+ return {
86
+ [this.apiKeyHeaderName]: this.apiKeyValue
87
+ };
88
+ }
83
89
  url(route, query) {
84
90
  return buildUrl(this.host, route, query);
85
91
  }
@@ -107,7 +113,7 @@ var HCSafeCDXClient = class {
107
113
  const envelope = await this.http.post(
108
114
  this.url("scan/2ddm"),
109
115
  wrapData(payload),
110
- this.getJsonAuthHeaders()
116
+ this.getJsonHeaders()
111
117
  );
112
118
  return assertApiResponse(envelope, "scan/2ddm");
113
119
  }
@@ -122,7 +128,7 @@ var HCSafeCDXClient = class {
122
128
  const envelope = await this.http.post(
123
129
  this.url("test/profile"),
124
130
  wrapData(payload),
125
- this.getJsonAuthHeaders()
131
+ this.getJsonHeaders()
126
132
  );
127
133
  return assertApiResponse(envelope, "test/profile");
128
134
  }
@@ -136,7 +142,7 @@ var HCSafeCDXClient = class {
136
142
  const envelope = await this.http.post(
137
143
  this.url("test/profiles/by-account"),
138
144
  wrapData(payload),
139
- this.getJsonAuthHeaders()
145
+ this.getJsonHeaders()
140
146
  );
141
147
  return assertApiResponse(envelope, "test/profiles/by-account");
142
148
  }
@@ -159,14 +165,16 @@ var HCSafeCDXClient = class {
159
165
  const envelope = await this.http.post(
160
166
  this.url("upload/url"),
161
167
  wrapData(payload),
162
- this.getJsonAuthHeaders()
168
+ this.getJsonHeaders()
163
169
  );
164
170
  return assertApiResponse(envelope, "upload/url");
165
171
  }
166
172
  /**
167
173
  * PUT preSignedURL — direct S3 upload, not a SafeCDX API route.
168
174
  * preSignedURL comes from createUploadUrl() response: Data.preSignedURL.
169
-
175
+ *
176
+ * This intentionally does not use the shared HttpClient because the existing
177
+ * HttpClient.put() is JSON-oriented and stringifies the request body.
170
178
  */
171
179
  async uploadImage(preSignedURL, image, contentType = "image/jpeg") {
172
180
  const response = await fetch(preSignedURL, {
@@ -207,7 +215,7 @@ var HCSafeCDXClient = class {
207
215
  return this.http.post(
208
216
  this.url("cvml/status"),
209
217
  wrapData(payload),
210
- this.getJsonAuthHeaders()
218
+ this.getJsonHeaders()
211
219
  );
212
220
  }
213
221
  /**
@@ -236,7 +244,7 @@ var HCSafeCDXClient = class {
236
244
  const envelope = await this.http.post(
237
245
  this.url("test/result/pending"),
238
246
  wrapData(payload),
239
- this.getJsonAuthHeaders()
247
+ this.getJsonHeaders()
240
248
  );
241
249
  return assertApiResponse(envelope, "test/result/pending");
242
250
  }
@@ -248,7 +256,7 @@ var HCSafeCDXClient = class {
248
256
  const envelope = await this.http.post(
249
257
  this.url("test/result/last"),
250
258
  wrapData(payload),
251
- this.getJsonAuthHeaders()
259
+ this.getJsonHeaders()
252
260
  );
253
261
  return assertApiResponse(envelope, "test/result/last");
254
262
  }
@@ -260,7 +268,7 @@ var HCSafeCDXClient = class {
260
268
  const envelope = await this.http.post(
261
269
  this.url("test/result/history"),
262
270
  wrapData(payload),
263
- this.getJsonAuthHeaders()
271
+ this.getJsonHeaders()
264
272
  );
265
273
  return assertApiResponse(envelope, "test/result/history");
266
274
  }
@@ -276,7 +284,7 @@ var HCSafeCDXClient = class {
276
284
  return this.http.post(
277
285
  this.url("test/result/details"),
278
286
  wrapData(payload),
279
- this.getJsonAuthHeaders()
287
+ this.getJsonHeaders()
280
288
  );
281
289
  }
282
290
  /**
@@ -287,7 +295,7 @@ var HCSafeCDXClient = class {
287
295
  const envelope = await this.http.post(
288
296
  this.url("test/result/pdf"),
289
297
  wrapData(payload),
290
- this.getJsonAuthHeaders()
298
+ this.getJsonHeaders()
291
299
  );
292
300
  return assertApiResponse(envelope, "test/result/pdf");
293
301
  }
@@ -303,7 +311,7 @@ var HCSafeCDXClient = class {
303
311
  return this.http.post(
304
312
  this.url("test/result/image/capture"),
305
313
  wrapData(payload),
306
- this.getJsonAuthHeaders()
314
+ this.getJsonHeaders()
307
315
  );
308
316
  }
309
317
  /**
@@ -314,7 +322,7 @@ var HCSafeCDXClient = class {
314
322
  const envelope = await this.http.post(
315
323
  this.url("test/result/analytics"),
316
324
  wrapData(payload),
317
- this.getJsonAuthHeaders()
325
+ this.getJsonHeaders()
318
326
  );
319
327
  return assertApiResponse(envelope, "test/result/analytics");
320
328
  }
@@ -333,7 +341,7 @@ var HCSafeCDXClient = class {
333
341
  const envelope = await this.http.post(
334
342
  this.url("test/resume"),
335
343
  wrapData(payload),
336
- this.getJsonAuthHeaders()
344
+ this.getJsonHeaders()
337
345
  );
338
346
  return assertApiResponse(envelope, "test/resume");
339
347
  }
@@ -345,7 +353,7 @@ var HCSafeCDXClient = class {
345
353
  const envelope = await this.http.post(
346
354
  this.url("test/answers"),
347
355
  wrapData(payload),
348
- this.getJsonAuthHeaders()
356
+ this.getJsonHeaders()
349
357
  );
350
358
  return assertApiResponse(envelope, "test/answers");
351
359
  }
@@ -357,7 +365,7 @@ var HCSafeCDXClient = class {
357
365
  const envelope = await this.http.post(
358
366
  this.url("test/finalize"),
359
367
  wrapData(payload),
360
- this.getJsonAuthHeaders()
368
+ this.getJsonHeaders()
361
369
  );
362
370
  return assertApiResponse(envelope, "test/finalize");
363
371
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@healthcloudai/hc-safe-cdx",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Healthcheck Safe CDX connector.",
5
5
  "author": "Healthcheck Systems Inc",
6
6
  "license": "MIT",
@@ -33,7 +33,8 @@
33
33
  "prepublishOnly": "npm run build"
34
34
  },
35
35
  "dependencies": {
36
- "@healthcloudai/hc-http": "^0.0.6"
36
+ "@healthcloudai/hc-http": "^0.0.6",
37
+ "@healthcloudai/hc-login-connector": "^0.0.15"
37
38
  },
38
39
  "peerDependencies": {
39
40
  "react-native": ">=0.70.0"