@healthcloudai/hc-login-connector 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,443 +20,411 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- AuthError: () => AuthError,
24
- ConfigError: () => ConfigError,
23
+ APIError: () => import_hc_http2.APIError,
24
+ ConfigError: () => import_hc_http2.ConfigError,
25
25
  HCLoginClient: () => HCLoginClient,
26
- HttpError: () => HttpError
26
+ HCServiceError: () => import_hc_http2.HCServiceError,
27
+ NetworkError: () => import_hc_http2.NetworkError,
28
+ ValidationError: () => import_hc_http2.ValidationError,
29
+ errorFromHttpStatus: () => import_hc_http2.errorFromHttpStatus
27
30
  });
28
31
  module.exports = __toCommonJS(index_exports);
29
32
 
30
- // src/errors.ts
31
- var ConfigError = class extends Error {
32
- constructor(message) {
33
- super(message);
34
- this.name = "ConfigError";
35
- }
36
- };
37
- var AuthError = class extends Error {
38
- constructor(message) {
39
- super(message);
40
- this.name = "AuthError";
41
- }
42
- };
43
- var HttpError = class extends Error {
44
- constructor(status, message) {
45
- super(message);
46
- this.name = "HttpError";
47
- this.status = status;
48
- }
49
- };
50
-
51
33
  // src/client.ts
52
- var ENV_PREFIX = {
53
- dev: "dev-api-healthcheck",
54
- uat: "uat-api-healthcheck",
55
- prod: "api-healthcheck"
34
+ var import_hc_http = require("@healthcloudai/hc-http");
35
+ var API_BASE_URLS = {
36
+ dev: "https://dev-api-healthcheck.healthcloud-services.com",
37
+ uat: "https://uat-api-healthcheck.healthcloud-services.com",
38
+ prod: "https://api-healthcheck.healthcloud-services.com"
56
39
  };
57
- function buildBaseUrl(tenantID, environment, region) {
58
- const regionSuffix = region ? `-${region}` : "";
59
- return `https://${ENV_PREFIX[environment]}${regionSuffix}.healthcloud-services.com/api`;
40
+ function buildBaseUrl(environment, region) {
41
+ const base = API_BASE_URLS[environment];
42
+ if (!region) {
43
+ return base;
44
+ }
45
+ return base.replace(
46
+ "https://",
47
+ `https://${region}-`
48
+ );
60
49
  }
61
50
  var HCLoginClient = class {
62
51
  constructor(httpClient) {
63
52
  this.http = httpClient;
64
53
  }
54
+ // =========================================================================
55
+ // Configuration
56
+ // =========================================================================
65
57
  configure(tenantID, environment, region) {
66
58
  const trimmedTenantID = tenantID == null ? void 0 : tenantID.trim();
67
59
  if (!trimmedTenantID) {
68
- throw new ConfigError("tenantID is required.");
60
+ throw new import_hc_http.ConfigError(
61
+ "tenantID is required."
62
+ );
69
63
  }
70
- if (!ENV_PREFIX[environment]) {
71
- throw new ConfigError("Invalid environment.");
64
+ const baseUrl = API_BASE_URLS[environment];
65
+ if (!baseUrl) {
66
+ throw new import_hc_http.ConfigError(
67
+ `Unsupported environment: "${environment}"`
68
+ );
72
69
  }
73
70
  this.config = {
74
71
  tenantID: trimmedTenantID,
75
72
  environment,
76
73
  region,
77
- baseUrl: buildBaseUrl(trimmedTenantID, environment, region)
74
+ baseUrl: buildBaseUrl(
75
+ environment,
76
+ region
77
+ )
78
78
  };
79
79
  }
80
80
  setApiKey(headerName, value) {
81
81
  const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
82
82
  const trimmedValue = value == null ? void 0 : value.trim();
83
83
  if (!trimmedHeaderName) {
84
- throw new ConfigError("API key header name is required.");
84
+ throw new import_hc_http.ConfigError(
85
+ "API key header name is required."
86
+ );
85
87
  }
86
88
  if (!trimmedValue) {
87
- throw new ConfigError("API key value is required.");
89
+ throw new import_hc_http.ConfigError(
90
+ "API key value is required."
91
+ );
88
92
  }
89
93
  this.apiKeyHeaderName = trimmedHeaderName;
90
94
  this.apiKeyValue = trimmedValue;
91
95
  }
92
96
  getEnvironment() {
93
- if (!this.config) {
94
- throw new ConfigError("Login client must be configured first.");
95
- }
97
+ this.ensureConfigured();
96
98
  return this.config.environment;
97
99
  }
98
- async register(email, password, firstName = "Unknown", lastName = "Unknown", options = {}) {
99
- var _a, _b;
100
+ getBaseUrl() {
100
101
  this.ensureConfigured();
101
- const config = this.config;
102
- const userEmail = (_a = options.email) != null ? _a : email;
103
- const attributes = {
104
- ...(_b = options.attributes) != null ? _b : {},
105
- ...options.verifyEmailCode === true ? { VERIFY_EMAIL_CODE: "true" } : {}
106
- };
107
- const requestPayload = {
108
- Data: {
109
- TenantID: config.tenantID,
110
- Credentials: {
111
- Email: email,
112
- Password: password
113
- },
114
- User: {
115
- FirstName: firstName || "Unknown",
116
- LastName: lastName || "Unknown",
117
- Email: userEmail,
118
- Phone: options.phone,
119
- BirthDate: options.birthDate,
120
- Gender: options.gender,
121
- Attributes: attributes
122
- }
123
- }
124
- };
125
- return this.http.post(
126
- `${config.baseUrl}/patient/register`,
127
- requestPayload,
128
- {
129
- "Content-Type": "application/json",
130
- "X-Tenant-ID": config.tenantID,
131
- ...this.getApiKeyHeader()
132
- }
133
- );
102
+ return this.config.baseUrl;
134
103
  }
135
- async registerFull(options = {}) {
136
- var _a, _b, _c, _d, _e;
104
+ getTenantId() {
137
105
  this.ensureConfigured();
138
- const config = this.config;
139
- const attributes = {
140
- ...(_a = options.attributes) != null ? _a : {},
141
- ...options.verifyEmailCode === true ? { VERIFY_EMAIL_CODE: "true" } : {}
142
- };
143
- const requestPayload = {
144
- Data: {
145
- TenantID: config.tenantID,
146
- Credentials: {
147
- Email: (_b = options.email) != null ? _b : "",
148
- Password: (_c = options.password) != null ? _c : "",
149
- TenantID: config.tenantID,
150
- ...options.language ? { Language: options.language } : {}
151
- },
152
- User: {
153
- FirstName: (_d = options.firstName) != null ? _d : "Unknown",
154
- LastName: (_e = options.lastName) != null ? _e : "Unknown",
155
- ...options.email ? { Email: options.email } : {},
156
- ...options.phone ? { Phone: options.phone } : {},
157
- ...options.birthDate ? { BirthDate: options.birthDate } : {},
158
- ...options.gender ? { Gender: options.gender } : {},
159
- ...options.middleName ? { MiddleName: options.middleName } : {},
160
- ...options.race ? { Race: options.race } : {},
161
- ...options.ethnicity ? { Ethnicity: options.ethnicity } : {},
162
- ...options.sex ? { Sex: options.sex } : {},
163
- ...options.genderIdentity ? { GenderIdentity: options.genderIdentity } : {},
164
- ...options.status !== void 0 ? { Status: options.status } : {},
165
- ...options.address ? { Address: options.address } : {},
166
- Attributes: attributes
167
- }
168
- }
169
- };
170
- return this.http.post(
171
- `${config.baseUrl}/patient/register`,
172
- requestPayload,
173
- {
174
- "Content-Type": "application/json",
175
- "X-Tenant-ID": config.tenantID,
176
- ...this.getApiKeyHeader()
177
- }
178
- );
179
- }
180
- async verifyEmail(email, code, language = "en", options = { verifyEmailCode: false }) {
181
- return this.submitOnboardingStep(
182
- "EMAIL_VERIFY",
183
- this.buildEmailVerificationInput(email, options),
184
- code,
185
- language
186
- );
187
- }
188
- async resendEmailVerify(email, language = "en", options = { verifyEmailCode: false }) {
189
- return this.submitOnboardingStep(
190
- "RESEND_EMAIL_VERIFY",
191
- this.buildEmailVerificationInput(email, options),
192
- "",
193
- language
194
- );
195
- }
196
- async resendSmsVerify(email, phone, language = "en") {
197
- return this.submitOnboardingStep(
198
- "RESEND_SMS_VERIFY",
199
- {
200
- Email: email,
201
- Phone: phone
202
- },
203
- "",
204
- language
205
- );
106
+ return this.config.tenantID;
206
107
  }
207
- async verifySms(email, phone, code, language = "en") {
208
- return this.submitOnboardingStep(
209
- "SMS_VERIFY",
210
- {
211
- Email: email,
212
- Phone: phone
213
- },
214
- code,
215
- language
216
- );
108
+ getTokens() {
109
+ return this.tokens;
217
110
  }
218
- async saveHealthProfile(profile, language = "en") {
219
- return this.submitOnboardingStep(
220
- "SAVE_HEALTH_PROFILE",
221
- profile,
222
- "",
223
- language
224
- );
111
+ getAccessToken() {
112
+ var _a;
113
+ return (_a = this.tokens) == null ? void 0 : _a.accessToken;
225
114
  }
226
- async saveAddress(email, address, language = "en") {
227
- return this.submitOnboardingStep(
228
- "SAVE_ADDRESS",
229
- {
230
- Email: email,
231
- Address: address
232
- },
233
- "",
234
- language
235
- );
115
+ getIDToken() {
116
+ var _a;
117
+ return (_a = this.tokens) == null ? void 0 : _a.idToken;
236
118
  }
237
- async login(email, password) {
119
+ getAuthHeader() {
238
120
  var _a;
239
121
  this.ensureConfigured();
240
- const requestPayload = {
241
- Data: {
242
- Email: email,
243
- Password: password,
244
- TenantID: this.config.tenantID
245
- }
246
- };
247
- const resp = await this.http.post(
248
- `${this.config.baseUrl}/patient/login`,
249
- requestPayload,
250
- {
251
- "X-Tenant-ID": this.config.tenantID,
252
- ...this.getApiKeyHeader()
253
- }
254
- );
255
- const data = (_a = resp.Data) != null ? _a : resp;
256
- const tokens = {
257
- accessToken: data.AccessToken,
258
- refreshToken: data.RefreshToken,
259
- idToken: data.IDToken,
260
- expiresIn: new Date(data.Expiration).getTime()
122
+ if (!((_a = this.tokens) == null ? void 0 : _a.idToken)) {
123
+ throw new import_hc_http.APIError({
124
+ message: "User is not authenticated.",
125
+ code: "UNAUTHORIZED",
126
+ statusCode: 401
127
+ });
128
+ }
129
+ return {
130
+ Authorization: `Bearer ${this.tokens.idToken}`,
131
+ "X-Tenant-ID": this.config.tenantID,
132
+ ...this.getApiKeyHeader()
261
133
  };
262
- this.tokens = tokens;
263
- return tokens;
264
134
  }
265
- async refreshToken() {
266
- var _a, _b;
267
- this.ensureConfigured();
268
- if (!((_a = this.tokens) == null ? void 0 : _a.refreshToken)) {
269
- throw new AuthError("No refresh token available");
135
+ logout() {
136
+ this.tokens = void 0;
137
+ }
138
+ // =========================================================================
139
+ // Authentication
140
+ // =========================================================================
141
+ async login(email, password, context = "PATIENT") {
142
+ if (!(email == null ? void 0 : email.trim())) {
143
+ throw new import_hc_http.ValidationError({
144
+ message: "Email is required.",
145
+ code: "INVALID_INPUT"
146
+ });
270
147
  }
271
- const requestPayload = {
272
- Data: {
273
- RefreshToken: this.tokens.refreshToken,
274
- TenantID: this.config.tenantID
275
- }
276
- };
277
- const resp = await this.http.post(
278
- `${this.config.baseUrl}/patient/refresh`,
279
- requestPayload,
280
- {
281
- "X-Tenant-ID": this.config.tenantID,
282
- ...this.getApiKeyHeader()
148
+ if (!(password == null ? void 0 : password.trim())) {
149
+ throw new import_hc_http.ValidationError({
150
+ message: "Password is required.",
151
+ code: "INVALID_INPUT"
152
+ });
153
+ }
154
+ this.ensureConfigured();
155
+ return this.execute(
156
+ "login",
157
+ async () => {
158
+ const response = await this.http.post(
159
+ `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/login`,
160
+ {
161
+ Data: {
162
+ Email: email,
163
+ Password: password,
164
+ TenantID: this.config.tenantID
165
+ }
166
+ },
167
+ this.headers()
168
+ );
169
+ if (!response.Data) {
170
+ throw new import_hc_http.APIError({
171
+ message: "login: identity authorization data is missing",
172
+ code: "INVALID_RESPONSE",
173
+ details: response
174
+ });
175
+ }
176
+ this.storeAuthTokens(
177
+ response.Data
178
+ );
179
+ return response;
283
180
  }
284
181
  );
285
- const data = (_b = resp.Data) != null ? _b : resp;
286
- const tokens = {
287
- accessToken: data.AccessToken,
288
- refreshToken: data.RefreshToken,
289
- idToken: data.IDToken,
290
- expiresIn: new Date(data.Expiration).getTime()
291
- };
292
- this.tokens = tokens;
293
- return tokens;
294
182
  }
295
- async resetPassword(email, isPasswordResetWithOTP = false) {
296
- var _a;
183
+ async refreshToken(refreshToken, context = "PATIENT") {
184
+ var _a, _b;
185
+ const token = (_b = refreshToken == null ? void 0 : refreshToken.trim()) != null ? _b : (_a = this.tokens) == null ? void 0 : _a.refreshToken;
186
+ if (!(token == null ? void 0 : token.trim())) {
187
+ throw new import_hc_http.ValidationError({
188
+ message: "Refresh token is required.",
189
+ code: "INVALID_INPUT"
190
+ });
191
+ }
297
192
  this.ensureConfigured();
298
- const requestPayload = {
299
- Data: {
300
- Email: email,
301
- TenantID: this.config.tenantID,
302
- ...isPasswordResetWithOTP === true ? { IsPasswordResetWithOTP: true } : {}
303
- }
304
- };
305
- await this.http.post(
306
- `${this.config.baseUrl}/patient/resetpassword`,
307
- requestPayload,
308
- {
309
- "Content-Type": "application/json",
310
- "X-Tenant-ID": this.config.tenantID,
311
- ...this.getApiKeyHeader(),
312
- ...((_a = this.tokens) == null ? void 0 : _a.idToken) ? { Authorization: `Bearer ${this.tokens.idToken}` } : {}
193
+ return this.execute(
194
+ "refreshToken",
195
+ async () => {
196
+ const requestPayload = {
197
+ Data: {
198
+ RefreshToken: token,
199
+ TenantID: this.config.tenantID
200
+ }
201
+ };
202
+ const response = await this.http.post(
203
+ `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/refresh`,
204
+ requestPayload,
205
+ this.headers()
206
+ );
207
+ if (!response.Data) {
208
+ throw new import_hc_http.APIError({
209
+ message: "refreshToken: identity authorization data is missing",
210
+ code: "INVALID_RESPONSE",
211
+ details: response
212
+ });
213
+ }
214
+ this.storeAuthTokens(
215
+ response.Data
216
+ );
217
+ return response;
313
218
  }
314
219
  );
315
220
  }
316
- async resetPasswordConfirm(payload) {
317
- var _a;
318
- this.ensureConfigured();
319
- const isOtpFlow = payload.Data.IsPasswordResetWithOTP === true;
320
- if (isOtpFlow && !payload.Data.Code) {
321
- throw new ConfigError("Code is required for OTP password reset confirmation.");
221
+ async register(email, password, firstName = "Unknown", lastName = "Unknown", options = {}) {
222
+ if (!(email == null ? void 0 : email.trim())) {
223
+ throw new import_hc_http.ValidationError({
224
+ message: "Email is required.",
225
+ code: "INVALID_INPUT"
226
+ });
322
227
  }
323
- if (!isOtpFlow && !payload.Data.Token) {
324
- throw new ConfigError("Token is required for link-based password reset confirmation.");
228
+ if (!(password == null ? void 0 : password.trim())) {
229
+ throw new import_hc_http.ValidationError({
230
+ message: "Password is required.",
231
+ code: "INVALID_INPUT"
232
+ });
325
233
  }
326
- await this.http.post(
327
- `${this.config.baseUrl}/patient/password`,
328
- {
329
- Data: {
330
- ...payload.Data,
331
- TenantID: this.config.tenantID
332
- }
333
- },
334
- {
335
- "Content-Type": "application/json",
336
- "X-Tenant-ID": this.config.tenantID,
337
- ...this.getApiKeyHeader(),
338
- ...((_a = this.tokens) == null ? void 0 : _a.idToken) ? { Authorization: `Bearer ${this.tokens.idToken}` } : {}
234
+ this.ensureConfigured();
235
+ return this.execute(
236
+ "register",
237
+ () => {
238
+ var _a, _b;
239
+ return this.http.post(
240
+ `${this.config.baseUrl}/api/patient/register`,
241
+ {
242
+ Data: {
243
+ TenantID: this.config.tenantID,
244
+ Credentials: {
245
+ Email: email,
246
+ Password: password
247
+ },
248
+ User: {
249
+ FirstName: firstName,
250
+ LastName: lastName,
251
+ Email: (_a = options.email) != null ? _a : email,
252
+ Phone: options.phone,
253
+ BirthDate: options.birthDate,
254
+ Gender: options.gender,
255
+ Attributes: {
256
+ ...(_b = options.attributes) != null ? _b : {},
257
+ ...options.verifyEmailCode === true ? {
258
+ VERIFY_EMAIL_CODE: "true"
259
+ } : {}
260
+ }
261
+ }
262
+ }
263
+ },
264
+ this.headers()
265
+ );
339
266
  }
340
267
  );
341
268
  }
342
- getAccessToken() {
343
- var _a;
344
- return (_a = this.tokens) == null ? void 0 : _a.accessToken;
345
- }
346
- getIDToken() {
347
- var _a;
348
- return (_a = this.tokens) == null ? void 0 : _a.idToken;
349
- }
350
- async getUserInfo() {
351
- var _a;
352
- this.ensureConfigured();
353
- if (!((_a = this.tokens) == null ? void 0 : _a.idToken)) {
354
- throw new AuthError("No ID token available");
269
+ async requestPasswordReset(email, context = "PATIENT", isOTP = false) {
270
+ if (!(email == null ? void 0 : email.trim())) {
271
+ throw new import_hc_http.ValidationError({
272
+ message: "Email is required.",
273
+ code: "INVALID_INPUT"
274
+ });
355
275
  }
356
- return this.http.get(
357
- `${this.config.baseUrl}/patient/header`,
358
- this.getAuthHeader()
276
+ this.ensureConfigured();
277
+ return this.execute(
278
+ "requestPasswordReset",
279
+ () => this.http.post(
280
+ `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/resetpassword`,
281
+ {
282
+ Data: {
283
+ Email: email,
284
+ TenantID: this.config.tenantID,
285
+ IsPasswordResetWithOTP: isOTP
286
+ }
287
+ },
288
+ this.headers()
289
+ )
359
290
  );
360
291
  }
361
- ensureConfigured() {
362
- if (!this.config) {
363
- throw new ConfigError("HCLogin is not configured. Call configure() first.");
292
+ async confirmPasswordReset(email, password, code, context = "PATIENT") {
293
+ if (!(email == null ? void 0 : email.trim())) {
294
+ throw new import_hc_http.ValidationError({
295
+ message: "Email is required.",
296
+ code: "INVALID_INPUT"
297
+ });
364
298
  }
365
- }
366
- buildOnboardingUser(step, user) {
367
- var _a;
368
- switch (step) {
369
- case "EMAIL_VERIFY":
370
- case "RESEND_EMAIL_VERIFY":
371
- return {
372
- Email: user.Email,
373
- TenantID: this.config.tenantID,
374
- ...user.Attributes ? { Attributes: user.Attributes } : {}
375
- };
376
- case "RESEND_SMS_VERIFY":
377
- case "SMS_VERIFY":
378
- return {
379
- Email: user.Email,
380
- TenantID: this.config.tenantID,
381
- Phone: user.Phone
382
- };
383
- case "SAVE_HEALTH_PROFILE":
384
- return {
385
- Email: user.Email,
386
- TenantID: this.config.tenantID,
387
- FirstName: user.FirstName,
388
- LastName: user.LastName,
389
- BirthDate: user.BirthDate,
390
- Gender: user.Gender,
391
- Sex: user.Sex,
392
- Status: (_a = user.Status) != null ? _a : 0,
393
- Phone: user.Phone,
394
- ...user.Race !== void 0 ? { Race: user.Race } : {},
395
- ...user.Ethnicity !== void 0 ? { Ethnicity: user.Ethnicity } : {}
396
- };
397
- case "SAVE_ADDRESS":
398
- return {
399
- Email: user.Email,
400
- TenantID: this.config.tenantID,
401
- Address: user.Address ? {
402
- StreetAndNumber: user.Address.StreetAndNumber,
403
- Extension: user.Address.Extension,
404
- City: user.Address.City,
405
- State: user.Address.State,
406
- PostalCode: user.Address.PostalCode,
407
- Country: user.Address.Country
408
- } : void 0
409
- };
299
+ if (!(password == null ? void 0 : password.trim())) {
300
+ throw new import_hc_http.ValidationError({
301
+ message: "Password is required.",
302
+ code: "INVALID_INPUT"
303
+ });
410
304
  }
411
- }
412
- buildEmailVerificationInput(email, options) {
413
- const attributes = this.buildEmailVerificationAttributes(options);
414
- return {
415
- Email: email,
416
- ...attributes ? { Attributes: attributes } : {}
417
- };
418
- }
419
- buildEmailVerificationAttributes(options) {
420
- var _a;
421
- const attributes = {
422
- ...(_a = options.attributes) != null ? _a : {}
423
- };
424
- if (options.verifyEmailCode === true) {
425
- attributes.VERIFY_EMAIL_CODE = "true";
305
+ if (!(code == null ? void 0 : code.trim())) {
306
+ throw new import_hc_http.ValidationError({
307
+ message: "Reset code is required.",
308
+ code: "INVALID_INPUT"
309
+ });
426
310
  }
427
- return Object.keys(attributes).length > 0 ? attributes : void 0;
311
+ this.ensureConfigured();
312
+ return this.execute(
313
+ "confirmPasswordReset",
314
+ () => this.http.post(
315
+ `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/password`,
316
+ {
317
+ Data: {
318
+ Email: email,
319
+ Password: password,
320
+ Code: code,
321
+ TenantID: this.config.tenantID
322
+ }
323
+ },
324
+ this.headers()
325
+ )
326
+ );
428
327
  }
429
- submitOnboardingStep(step, user, data = "", language = "en") {
328
+ async getPatientHeader() {
430
329
  this.ensureConfigured();
431
- const payload = {
432
- Data: {
433
- Data: data,
434
- Step: step,
435
- User: this.buildOnboardingUser(step, user),
436
- Language: language
330
+ return this.execute(
331
+ "getPatientHeader",
332
+ () => this.http.get(
333
+ `${this.config.baseUrl}/api/patient/header`,
334
+ this.authHeaders()
335
+ )
336
+ );
337
+ }
338
+ // =========================================================================
339
+ // Private
340
+ // =========================================================================
341
+ async execute(operation, request) {
342
+ let response;
343
+ try {
344
+ response = await request();
345
+ } catch (err) {
346
+ if (err instanceof import_hc_http.APIError) {
347
+ throw err;
437
348
  }
438
- };
439
- return this.http.post(
440
- `${this.config.baseUrl}/patient/onboard`,
441
- payload,
442
- {
443
- "Content-Type": "application/json",
444
- "X-Tenant-ID": this.config.tenantID,
445
- ...this.getApiKeyHeader()
349
+ if (err instanceof TypeError) {
350
+ throw new import_hc_http.NetworkError({
351
+ message: `${operation}: network request failed`,
352
+ code: "NETWORK_ERROR",
353
+ details: err
354
+ });
355
+ }
356
+ if (err instanceof DOMException) {
357
+ if (err.name === "AbortError") {
358
+ throw new import_hc_http.NetworkError({
359
+ message: `${operation}: request aborted`,
360
+ code: "REQUEST_ABORTED",
361
+ details: err
362
+ });
363
+ }
364
+ }
365
+ if (err instanceof Error) {
366
+ throw new import_hc_http.APIError({
367
+ message: `${operation}: ${err.message}`,
368
+ code: "UNKNOWN_ERROR",
369
+ details: err
370
+ });
446
371
  }
372
+ throw new import_hc_http.APIError({
373
+ message: `${operation}: unexpected runtime failure`,
374
+ code: "UNKNOWN_ERROR",
375
+ details: err
376
+ });
377
+ }
378
+ if (response == null) {
379
+ throw new import_hc_http.APIError({
380
+ message: `${operation}: empty response received`,
381
+ code: "EMPTY_RESPONSE",
382
+ details: response
383
+ });
384
+ }
385
+ if (!this.isApiResponse(response)) {
386
+ throw new import_hc_http.APIError({
387
+ message: `${operation}: invalid API response structure`,
388
+ code: "INVALID_RESPONSE",
389
+ details: response
390
+ });
391
+ }
392
+ if (!response.IsOK) {
393
+ throw this.mapBackendError(
394
+ operation,
395
+ response
396
+ );
397
+ }
398
+ return response;
399
+ }
400
+ mapBackendError(operation, response) {
401
+ return new import_hc_http.HCServiceError(
402
+ operation,
403
+ response.ErrorMessage || "Unknown backend error",
404
+ response
447
405
  );
448
406
  }
449
- getAuthHeader() {
450
- var _a;
451
- if (!((_a = this.tokens) == null ? void 0 : _a.idToken)) {
452
- throw new AuthError("No ID token available");
407
+ isApiResponse(value) {
408
+ if (!value || typeof value !== "object") {
409
+ return false;
453
410
  }
411
+ const response = value;
412
+ return "IsOK" in response && typeof response.IsOK === "boolean" && "Data" in response && "ErrorMessage" in response;
413
+ }
414
+ headers() {
454
415
  return {
455
- Authorization: `Bearer ${this.tokens.idToken}`,
416
+ Accept: "application/json",
417
+ "Content-Type": "application/json",
456
418
  "X-Tenant-ID": this.config.tenantID,
457
419
  ...this.getApiKeyHeader()
458
420
  };
459
421
  }
422
+ authHeaders() {
423
+ return {
424
+ ...this.headers(),
425
+ ...this.getAuthHeader()
426
+ };
427
+ }
460
428
  getApiKeyHeader() {
461
429
  if (!this.apiKeyHeaderName || !this.apiKeyValue) {
462
430
  return {};
@@ -465,24 +433,59 @@ var HCLoginClient = class {
465
433
  [this.apiKeyHeaderName]: this.apiKeyValue
466
434
  };
467
435
  }
468
- getBaseUrl() {
436
+ resolveContextPath(context) {
437
+ switch (context) {
438
+ case "PATIENT":
439
+ return "patient";
440
+ case "PROVIDER":
441
+ return "provider";
442
+ case "KB":
443
+ return "kb";
444
+ case "ADMIN_PLATFORM":
445
+ return "admin/platform";
446
+ default:
447
+ return "patient";
448
+ }
449
+ }
450
+ ensureConfigured() {
469
451
  if (!this.config) {
470
- throw new ConfigError("Not configured");
452
+ throw new import_hc_http.ConfigError(
453
+ "Login client must be configured before use."
454
+ );
471
455
  }
472
- return this.config.baseUrl;
473
456
  }
474
- getTenantId() {
457
+ storeAuthTokens(raw) {
458
+ var _a;
459
+ this.tokens = {
460
+ accessToken: raw.AccessToken,
461
+ refreshToken: raw.RefreshToken,
462
+ idToken: (_a = raw.IDToken) != null ? _a : null,
463
+ expiresAt: this.resolveExpiration(
464
+ raw
465
+ )
466
+ };
467
+ }
468
+ resolveExpiration(raw) {
475
469
  var _a;
476
- if (!((_a = this.config) == null ? void 0 : _a.tenantID)) {
477
- throw new ConfigError("Not configured");
470
+ if (raw.Expiration) {
471
+ const parsed = new Date(
472
+ raw.Expiration
473
+ ).getTime();
474
+ return Number.isNaN(parsed) ? 0 : parsed;
478
475
  }
479
- return this.config.tenantID;
476
+ return (_a = raw.ExpiresIn) != null ? _a : 0;
480
477
  }
481
478
  };
479
+
480
+ // src/errors.ts
481
+ var import_hc_http2 = require("@healthcloudai/hc-http");
482
482
  // Annotate the CommonJS export names for ESM import in node:
483
483
  0 && (module.exports = {
484
- AuthError,
484
+ APIError,
485
485
  ConfigError,
486
486
  HCLoginClient,
487
- HttpError
487
+ HCServiceError,
488
+ NetworkError,
489
+ ValidationError,
490
+ errorFromHttpStatus
488
491
  });