@healthcloudai/hc-login-connector 0.0.15 → 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,437 +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
- async register(email, password, firstName = "Unknown", lastName = "Unknown", options = {}) {
93
- var _a, _b;
96
+ getEnvironment() {
94
97
  this.ensureConfigured();
95
- const config = this.config;
96
- const userEmail = (_a = options.email) != null ? _a : email;
97
- const attributes = {
98
- ...(_b = options.attributes) != null ? _b : {},
99
- ...options.verifyEmailCode === true ? { VERIFY_EMAIL_CODE: "true" } : {}
100
- };
101
- const requestPayload = {
102
- Data: {
103
- TenantID: config.tenantID,
104
- Credentials: {
105
- Email: email,
106
- Password: password
107
- },
108
- User: {
109
- FirstName: firstName || "Unknown",
110
- LastName: lastName || "Unknown",
111
- Email: userEmail,
112
- Phone: options.phone,
113
- BirthDate: options.birthDate,
114
- Gender: options.gender,
115
- Attributes: attributes
116
- }
117
- }
118
- };
119
- return this.http.post(
120
- `${config.baseUrl}/patient/register`,
121
- requestPayload,
122
- {
123
- "Content-Type": "application/json",
124
- "X-Tenant-ID": config.tenantID,
125
- ...this.getApiKeyHeader()
126
- }
127
- );
98
+ return this.config.environment;
128
99
  }
129
- async registerFull(options = {}) {
130
- var _a, _b, _c, _d, _e;
100
+ getBaseUrl() {
131
101
  this.ensureConfigured();
132
- const config = this.config;
133
- const attributes = {
134
- ...(_a = options.attributes) != null ? _a : {},
135
- ...options.verifyEmailCode === true ? { VERIFY_EMAIL_CODE: "true" } : {}
136
- };
137
- const requestPayload = {
138
- Data: {
139
- TenantID: config.tenantID,
140
- Credentials: {
141
- Email: (_b = options.email) != null ? _b : "",
142
- Password: (_c = options.password) != null ? _c : "",
143
- TenantID: config.tenantID,
144
- ...options.language ? { Language: options.language } : {}
145
- },
146
- User: {
147
- FirstName: (_d = options.firstName) != null ? _d : "Unknown",
148
- LastName: (_e = options.lastName) != null ? _e : "Unknown",
149
- ...options.email ? { Email: options.email } : {},
150
- ...options.phone ? { Phone: options.phone } : {},
151
- ...options.birthDate ? { BirthDate: options.birthDate } : {},
152
- ...options.gender ? { Gender: options.gender } : {},
153
- ...options.middleName ? { MiddleName: options.middleName } : {},
154
- ...options.race ? { Race: options.race } : {},
155
- ...options.ethnicity ? { Ethnicity: options.ethnicity } : {},
156
- ...options.sex ? { Sex: options.sex } : {},
157
- ...options.genderIdentity ? { GenderIdentity: options.genderIdentity } : {},
158
- ...options.status !== void 0 ? { Status: options.status } : {},
159
- ...options.address ? { Address: options.address } : {},
160
- Attributes: attributes
161
- }
162
- }
163
- };
164
- return this.http.post(
165
- `${config.baseUrl}/patient/register`,
166
- requestPayload,
167
- {
168
- "Content-Type": "application/json",
169
- "X-Tenant-ID": config.tenantID,
170
- ...this.getApiKeyHeader()
171
- }
172
- );
173
- }
174
- async verifyEmail(email, code, language = "en", options = { verifyEmailCode: false }) {
175
- return this.submitOnboardingStep(
176
- "EMAIL_VERIFY",
177
- this.buildEmailVerificationInput(email, options),
178
- code,
179
- language
180
- );
102
+ return this.config.baseUrl;
181
103
  }
182
- async resendEmailVerify(email, language = "en", options = { verifyEmailCode: false }) {
183
- return this.submitOnboardingStep(
184
- "RESEND_EMAIL_VERIFY",
185
- this.buildEmailVerificationInput(email, options),
186
- "",
187
- language
188
- );
104
+ getTenantId() {
105
+ this.ensureConfigured();
106
+ return this.config.tenantID;
189
107
  }
190
- async resendSmsVerify(email, phone, language = "en") {
191
- return this.submitOnboardingStep(
192
- "RESEND_SMS_VERIFY",
193
- {
194
- Email: email,
195
- Phone: phone
196
- },
197
- "",
198
- language
199
- );
108
+ getTokens() {
109
+ return this.tokens;
200
110
  }
201
- async verifySms(email, phone, code, language = "en") {
202
- return this.submitOnboardingStep(
203
- "SMS_VERIFY",
204
- {
205
- Email: email,
206
- Phone: phone
207
- },
208
- code,
209
- language
210
- );
211
- }
212
- async saveHealthProfile(profile, language = "en") {
213
- return this.submitOnboardingStep(
214
- "SAVE_HEALTH_PROFILE",
215
- profile,
216
- "",
217
- language
218
- );
111
+ getAccessToken() {
112
+ var _a;
113
+ return (_a = this.tokens) == null ? void 0 : _a.accessToken;
219
114
  }
220
- async saveAddress(email, address, language = "en") {
221
- return this.submitOnboardingStep(
222
- "SAVE_ADDRESS",
223
- {
224
- Email: email,
225
- Address: address
226
- },
227
- "",
228
- language
229
- );
115
+ getIDToken() {
116
+ var _a;
117
+ return (_a = this.tokens) == null ? void 0 : _a.idToken;
230
118
  }
231
- async login(email, password) {
119
+ getAuthHeader() {
232
120
  var _a;
233
121
  this.ensureConfigured();
234
- const requestPayload = {
235
- Data: {
236
- Email: email,
237
- Password: password,
238
- TenantID: this.config.tenantID
239
- }
240
- };
241
- const resp = await this.http.post(
242
- `${this.config.baseUrl}/patient/login`,
243
- requestPayload,
244
- {
245
- "X-Tenant-ID": this.config.tenantID,
246
- ...this.getApiKeyHeader()
247
- }
248
- );
249
- const data = (_a = resp.Data) != null ? _a : resp;
250
- const tokens = {
251
- accessToken: data.AccessToken,
252
- refreshToken: data.RefreshToken,
253
- idToken: data.IDToken,
254
- 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()
255
133
  };
256
- this.tokens = tokens;
257
- return tokens;
258
134
  }
259
- async refreshToken() {
260
- var _a, _b;
261
- this.ensureConfigured();
262
- if (!((_a = this.tokens) == null ? void 0 : _a.refreshToken)) {
263
- 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
+ });
264
147
  }
265
- const requestPayload = {
266
- Data: {
267
- RefreshToken: this.tokens.refreshToken,
268
- TenantID: this.config.tenantID
269
- }
270
- };
271
- const resp = await this.http.post(
272
- `${this.config.baseUrl}/patient/refresh`,
273
- requestPayload,
274
- {
275
- "X-Tenant-ID": this.config.tenantID,
276
- ...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;
277
180
  }
278
181
  );
279
- const data = (_b = resp.Data) != null ? _b : resp;
280
- const tokens = {
281
- accessToken: data.AccessToken,
282
- refreshToken: data.RefreshToken,
283
- idToken: data.IDToken,
284
- expiresIn: new Date(data.Expiration).getTime()
285
- };
286
- this.tokens = tokens;
287
- return tokens;
288
182
  }
289
- async resetPassword(email, isPasswordResetWithOTP = false) {
290
- 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
+ }
291
192
  this.ensureConfigured();
292
- const requestPayload = {
293
- Data: {
294
- Email: email,
295
- TenantID: this.config.tenantID,
296
- ...isPasswordResetWithOTP === true ? { IsPasswordResetWithOTP: true } : {}
297
- }
298
- };
299
- await this.http.post(
300
- `${this.config.baseUrl}/patient/resetpassword`,
301
- requestPayload,
302
- {
303
- "Content-Type": "application/json",
304
- "X-Tenant-ID": this.config.tenantID,
305
- ...this.getApiKeyHeader(),
306
- ...((_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;
307
218
  }
308
219
  );
309
220
  }
310
- async resetPasswordConfirm(payload) {
311
- var _a;
312
- this.ensureConfigured();
313
- const isOtpFlow = payload.Data.IsPasswordResetWithOTP === true;
314
- if (isOtpFlow && !payload.Data.Code) {
315
- 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
+ });
316
227
  }
317
- if (!isOtpFlow && !payload.Data.Token) {
318
- 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
+ });
319
233
  }
320
- await this.http.post(
321
- `${this.config.baseUrl}/patient/password`,
322
- {
323
- Data: {
324
- ...payload.Data,
325
- TenantID: this.config.tenantID
326
- }
327
- },
328
- {
329
- "Content-Type": "application/json",
330
- "X-Tenant-ID": this.config.tenantID,
331
- ...this.getApiKeyHeader(),
332
- ...((_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
+ );
333
266
  }
334
267
  );
335
268
  }
336
- getAccessToken() {
337
- var _a;
338
- return (_a = this.tokens) == null ? void 0 : _a.accessToken;
339
- }
340
- getIDToken() {
341
- var _a;
342
- return (_a = this.tokens) == null ? void 0 : _a.idToken;
343
- }
344
- async getUserInfo() {
345
- var _a;
346
- this.ensureConfigured();
347
- if (!((_a = this.tokens) == null ? void 0 : _a.idToken)) {
348
- 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
+ });
349
275
  }
350
- return this.http.get(
351
- `${this.config.baseUrl}/patient/header`,
352
- 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
+ )
353
290
  );
354
291
  }
355
- ensureConfigured() {
356
- if (!this.config) {
357
- 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
+ });
358
298
  }
359
- }
360
- buildOnboardingUser(step, user) {
361
- var _a;
362
- switch (step) {
363
- case "EMAIL_VERIFY":
364
- case "RESEND_EMAIL_VERIFY":
365
- return {
366
- Email: user.Email,
367
- TenantID: this.config.tenantID,
368
- ...user.Attributes ? { Attributes: user.Attributes } : {}
369
- };
370
- case "RESEND_SMS_VERIFY":
371
- case "SMS_VERIFY":
372
- return {
373
- Email: user.Email,
374
- TenantID: this.config.tenantID,
375
- Phone: user.Phone
376
- };
377
- case "SAVE_HEALTH_PROFILE":
378
- return {
379
- Email: user.Email,
380
- TenantID: this.config.tenantID,
381
- FirstName: user.FirstName,
382
- LastName: user.LastName,
383
- BirthDate: user.BirthDate,
384
- Gender: user.Gender,
385
- Sex: user.Sex,
386
- Status: (_a = user.Status) != null ? _a : 0,
387
- Phone: user.Phone,
388
- ...user.Race !== void 0 ? { Race: user.Race } : {},
389
- ...user.Ethnicity !== void 0 ? { Ethnicity: user.Ethnicity } : {}
390
- };
391
- case "SAVE_ADDRESS":
392
- return {
393
- Email: user.Email,
394
- TenantID: this.config.tenantID,
395
- Address: user.Address ? {
396
- StreetAndNumber: user.Address.StreetAndNumber,
397
- Extension: user.Address.Extension,
398
- City: user.Address.City,
399
- State: user.Address.State,
400
- PostalCode: user.Address.PostalCode,
401
- Country: user.Address.Country
402
- } : void 0
403
- };
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
+ });
404
304
  }
405
- }
406
- buildEmailVerificationInput(email, options) {
407
- const attributes = this.buildEmailVerificationAttributes(options);
408
- return {
409
- Email: email,
410
- ...attributes ? { Attributes: attributes } : {}
411
- };
412
- }
413
- buildEmailVerificationAttributes(options) {
414
- var _a;
415
- const attributes = {
416
- ...(_a = options.attributes) != null ? _a : {}
417
- };
418
- if (options.verifyEmailCode === true) {
419
- 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
+ });
420
310
  }
421
- 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
+ );
422
327
  }
423
- submitOnboardingStep(step, user, data = "", language = "en") {
328
+ async getPatientHeader() {
424
329
  this.ensureConfigured();
425
- const payload = {
426
- Data: {
427
- Data: data,
428
- Step: step,
429
- User: this.buildOnboardingUser(step, user),
430
- 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;
431
348
  }
432
- };
433
- return this.http.post(
434
- `${this.config.baseUrl}/patient/onboard`,
435
- payload,
436
- {
437
- "Content-Type": "application/json",
438
- "X-Tenant-ID": this.config.tenantID,
439
- ...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
+ });
440
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
441
405
  );
442
406
  }
443
- getAuthHeader() {
444
- var _a;
445
- if (!((_a = this.tokens) == null ? void 0 : _a.idToken)) {
446
- throw new AuthError("No ID token available");
407
+ isApiResponse(value) {
408
+ if (!value || typeof value !== "object") {
409
+ return false;
447
410
  }
411
+ const response = value;
412
+ return "IsOK" in response && typeof response.IsOK === "boolean" && "Data" in response && "ErrorMessage" in response;
413
+ }
414
+ headers() {
448
415
  return {
449
- Authorization: `Bearer ${this.tokens.idToken}`,
416
+ Accept: "application/json",
417
+ "Content-Type": "application/json",
450
418
  "X-Tenant-ID": this.config.tenantID,
451
419
  ...this.getApiKeyHeader()
452
420
  };
453
421
  }
422
+ authHeaders() {
423
+ return {
424
+ ...this.headers(),
425
+ ...this.getAuthHeader()
426
+ };
427
+ }
454
428
  getApiKeyHeader() {
455
429
  if (!this.apiKeyHeaderName || !this.apiKeyValue) {
456
430
  return {};
@@ -459,24 +433,59 @@ var HCLoginClient = class {
459
433
  [this.apiKeyHeaderName]: this.apiKeyValue
460
434
  };
461
435
  }
462
- 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() {
463
451
  if (!this.config) {
464
- throw new ConfigError("Not configured");
452
+ throw new import_hc_http.ConfigError(
453
+ "Login client must be configured before use."
454
+ );
465
455
  }
466
- return this.config.baseUrl;
467
456
  }
468
- 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) {
469
469
  var _a;
470
- if (!((_a = this.config) == null ? void 0 : _a.tenantID)) {
471
- 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;
472
475
  }
473
- return this.config.tenantID;
476
+ return (_a = raw.ExpiresIn) != null ? _a : 0;
474
477
  }
475
478
  };
479
+
480
+ // src/errors.ts
481
+ var import_hc_http2 = require("@healthcloudai/hc-http");
476
482
  // Annotate the CommonJS export names for ESM import in node:
477
483
  0 && (module.exports = {
478
- AuthError,
484
+ APIError,
479
485
  ConfigError,
480
486
  HCLoginClient,
481
- HttpError
487
+ HCServiceError,
488
+ NetworkError,
489
+ ValidationError,
490
+ errorFromHttpStatus
482
491
  });