@healthcloudai/hc-login-connector 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -114,7 +114,7 @@ The usage example above reads the value from local state.
114
114
  `getBaseUrl()` returns the currently configured base URL from local state.
115
115
 
116
116
  ```txt
117
- https://dev-api-healthcheck.healthcloud-services.com/api
117
+ https://dev-api-healthcheck.healthcloud-services.com
118
118
  ```
119
119
 
120
120
  ---
@@ -247,6 +247,10 @@ list expected by the integration.
247
247
  - If `verifyEmailCode` is `true`, the client sends `VERIFY_EMAIL_CODE: "true"`.
248
248
  - If `verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
249
249
 
250
+ #### OTP-based registration
251
+
252
+ Use this when email verification should be handled through an OTP code. In this case, `verifyEmailCode` should be `true`.
253
+
250
254
  ```ts
251
255
  await loginClient.register(
252
256
  "john.smith@example.com",
@@ -268,7 +272,7 @@ await loginClient.register(
268
272
  ```
269
273
 
270
274
  #### Full API request
271
- Request sent for the usage example above:
275
+ Request sent for the OTP example above:
272
276
 
273
277
  ```json
274
278
  {
@@ -295,6 +299,39 @@ Request sent for the usage example above:
295
299
  }
296
300
  ```
297
301
 
302
+ #### Link-based registration
303
+
304
+ Use this when email verification should be handled through an email link. In this case, `verifyEmailCode` should be omitted or `false`.
305
+
306
+ ```ts
307
+ await loginClient.register(
308
+ "john.smith@example.com",
309
+ "ExamplePassword123!",
310
+ "John",
311
+ "Smith"
312
+ );
313
+ ```
314
+
315
+ Effective request body:
316
+
317
+ ```json
318
+ {
319
+ "Data": {
320
+ "TenantID": "test-tenant",
321
+ "Credentials": {
322
+ "Email": "john.smith@example.com",
323
+ "Password": "ExamplePassword123!"
324
+ },
325
+ "User": {
326
+ "FirstName": "John",
327
+ "LastName": "Smith",
328
+ "Email": "john.smith@example.com",
329
+ "Attributes": {}
330
+ }
331
+ }
332
+ }
333
+ ```
334
+
298
335
  #### API response
299
336
 
300
337
  Status:
@@ -332,6 +369,10 @@ Registers a patient for the configured tenant using the richer registration payl
332
369
  `verifyEmailCode` behaves the same way as in `register(...)`:
333
370
  when enabled, the client injects `VERIFY_EMAIL_CODE: "true"` into `User.Attributes`.
334
371
 
372
+ #### OTP-based registration
373
+
374
+ Use this when email verification should be handled through an OTP code. In this case, `verifyEmailCode` should be `true`.
375
+
335
376
  ```ts
336
377
  await loginClient.registerFull({
337
378
  email: "john.smith@example.com",
@@ -406,6 +447,40 @@ Request sent for the usage example above:
406
447
  }
407
448
  ```
408
449
 
450
+ #### Link-based registration
451
+
452
+ Use this when email verification should be handled through an email link. In this case, `verifyEmailCode` should be omitted or `false`.
453
+
454
+ ```ts
455
+ await loginClient.registerFull({
456
+ email: "john.smith@example.com",
457
+ password: "ExamplePassword123!",
458
+ firstName: "John",
459
+ lastName: "Smith"
460
+ });
461
+ ```
462
+
463
+ Effective request body:
464
+
465
+ ```json
466
+ {
467
+ "Data": {
468
+ "TenantID": "test-tenant",
469
+ "Credentials": {
470
+ "Email": "john.smith@example.com",
471
+ "Password": "ExamplePassword123!",
472
+ "TenantID": "test-tenant"
473
+ },
474
+ "User": {
475
+ "FirstName": "John",
476
+ "LastName": "Smith",
477
+ "Email": "john.smith@example.com",
478
+ "Attributes": {}
479
+ }
480
+ }
481
+ }
482
+ ```
483
+
409
484
  #### API response
410
485
 
411
486
  Status:
@@ -440,6 +515,10 @@ when email verification should be handled through an OTP code, the client sends
440
515
  - If `options.verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
441
516
  - Calling `verifyEmail(...)` without `options` keeps OTP email verification disabled by default.
442
517
 
518
+ #### OTP-based email verification
519
+
520
+ Use this when email verification should be handled through an OTP code. In this case, `options.verifyEmailCode` should be `true`.
521
+
443
522
  ```ts
444
523
  await loginClient.verifyEmail(
445
524
  "john.smith@example.com",
@@ -452,7 +531,7 @@ await loginClient.verifyEmail(
452
531
  ```
453
532
 
454
533
  #### Full API request
455
- Request sent for the usage example above:
534
+ Request sent for the OTP example above:
456
535
 
457
536
  ```json
458
537
  {
@@ -471,6 +550,34 @@ Request sent for the usage example above:
471
550
  }
472
551
  ```
473
552
 
553
+ #### Link-based email verification
554
+
555
+ Use this when email verification should be handled through an email link. In this case, `options.verifyEmailCode` should be omitted or `false`.
556
+
557
+ ```ts
558
+ await loginClient.verifyEmail(
559
+ "john.smith@example.com",
560
+ "123456",
561
+ "en"
562
+ );
563
+ ```
564
+
565
+ Effective request body:
566
+
567
+ ```json
568
+ {
569
+ "Data": {
570
+ "Data": "123456",
571
+ "Step": "EMAIL_VERIFY",
572
+ "User": {
573
+ "Email": "john.smith@example.com",
574
+ "TenantID": "test-tenant"
575
+ },
576
+ "Language": "en"
577
+ }
578
+ }
579
+ ```
580
+
474
581
  #### API response
475
582
 
476
583
  Status:
@@ -505,6 +612,10 @@ when email verification should be handled through an OTP code, the client sends
505
612
  - If `options.verifyEmailCode` is `false` or omitted, `VERIFY_EMAIL_CODE` is not sent.
506
613
  - Calling `resendEmailVerify(...)` without `options` keeps OTP email verification disabled by default.
507
614
 
615
+ #### OTP-based resend
616
+
617
+ Use this when email verification should be handled through an OTP code. In this case, `options.verifyEmailCode` should be `true`.
618
+
508
619
  ```ts
509
620
  await loginClient.resendEmailVerify(
510
621
  "john.smith@example.com",
@@ -516,7 +627,7 @@ await loginClient.resendEmailVerify(
516
627
  ```
517
628
 
518
629
  #### Full API request
519
- Request sent for the usage example above:
630
+ Request sent for the OTP example above:
520
631
 
521
632
  ```json
522
633
  {
@@ -535,6 +646,33 @@ Request sent for the usage example above:
535
646
  }
536
647
  ```
537
648
 
649
+ #### Link-based resend
650
+
651
+ Use this when email verification should be handled through an email link. In this case, `options.verifyEmailCode` should be omitted or `false`.
652
+
653
+ ```ts
654
+ await loginClient.resendEmailVerify(
655
+ "john.smith@example.com",
656
+ "en"
657
+ );
658
+ ```
659
+
660
+ Effective request body:
661
+
662
+ ```json
663
+ {
664
+ "Data": {
665
+ "Data": "",
666
+ "Step": "RESEND_EMAIL_VERIFY",
667
+ "User": {
668
+ "Email": "john.smith@example.com",
669
+ "TenantID": "test-tenant"
670
+ },
671
+ "Language": "en"
672
+ }
673
+ }
674
+ ```
675
+
538
676
  #### API response
539
677
 
540
678
  Status:
package/dist/index.cjs CHANGED
@@ -138,7 +138,7 @@ var HCLoginClient = class {
138
138
  // =========================================================================
139
139
  // Authentication
140
140
  // =========================================================================
141
- async login(email, password, context = "PATIENT") {
141
+ async login(email, password) {
142
142
  if (!(email == null ? void 0 : email.trim())) {
143
143
  throw new import_hc_http.ValidationError({
144
144
  message: "Email is required.",
@@ -156,7 +156,7 @@ var HCLoginClient = class {
156
156
  "login",
157
157
  async () => {
158
158
  const response = await this.http.post(
159
- `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/login`,
159
+ `${this.config.baseUrl}/api/patient/login`,
160
160
  {
161
161
  Data: {
162
162
  Email: email,
@@ -180,7 +180,7 @@ var HCLoginClient = class {
180
180
  }
181
181
  );
182
182
  }
183
- async refreshToken(refreshToken, context = "PATIENT") {
183
+ async refreshToken(refreshToken) {
184
184
  var _a, _b;
185
185
  const token = (_b = refreshToken == null ? void 0 : refreshToken.trim()) != null ? _b : (_a = this.tokens) == null ? void 0 : _a.refreshToken;
186
186
  if (!(token == null ? void 0 : token.trim())) {
@@ -200,7 +200,7 @@ var HCLoginClient = class {
200
200
  }
201
201
  };
202
202
  const response = await this.http.post(
203
- `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/refresh`,
203
+ `${this.config.baseUrl}/api/patient/refresh`,
204
204
  requestPayload,
205
205
  this.headers()
206
206
  );
@@ -266,7 +266,7 @@ var HCLoginClient = class {
266
266
  }
267
267
  );
268
268
  }
269
- async requestPasswordReset(email, context = "PATIENT", isOTP = false) {
269
+ async requestPasswordReset(email, isOTP = false) {
270
270
  if (!(email == null ? void 0 : email.trim())) {
271
271
  throw new import_hc_http.ValidationError({
272
272
  message: "Email is required.",
@@ -277,19 +277,19 @@ var HCLoginClient = class {
277
277
  return this.execute(
278
278
  "requestPasswordReset",
279
279
  () => this.http.post(
280
- `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/resetpassword`,
280
+ `${this.config.baseUrl}/api/patient/resetpassword`,
281
281
  {
282
282
  Data: {
283
283
  Email: email,
284
284
  TenantID: this.config.tenantID,
285
- IsPasswordResetWithOTP: isOTP
285
+ ...isOTP ? { IsPasswordResetWithOTP: true } : {}
286
286
  }
287
287
  },
288
288
  this.headers()
289
289
  )
290
290
  );
291
291
  }
292
- async confirmPasswordReset(email, password, code, context = "PATIENT") {
292
+ async confirmPasswordReset(email, password, code) {
293
293
  if (!(email == null ? void 0 : email.trim())) {
294
294
  throw new import_hc_http.ValidationError({
295
295
  message: "Email is required.",
@@ -312,7 +312,7 @@ var HCLoginClient = class {
312
312
  return this.execute(
313
313
  "confirmPasswordReset",
314
314
  () => this.http.post(
315
- `${this.config.baseUrl}/api/${this.resolveContextPath(context)}/password`,
315
+ `${this.config.baseUrl}/api/patient/password`,
316
316
  {
317
317
  Data: {
318
318
  Email: email,
@@ -336,6 +336,146 @@ var HCLoginClient = class {
336
336
  );
337
337
  }
338
338
  // =========================================================================
339
+ // Registration (full)
340
+ // =========================================================================
341
+ async registerFull(options = {}) {
342
+ var _a;
343
+ this.ensureConfigured();
344
+ const attributes = {
345
+ ...(_a = options.attributes) != null ? _a : {},
346
+ ...options.verifyEmailCode === true ? { VERIFY_EMAIL_CODE: "true" } : {}
347
+ };
348
+ return this.execute(
349
+ "registerFull",
350
+ () => {
351
+ var _a2, _b, _c, _d;
352
+ return this.http.post(
353
+ `${this.config.baseUrl}/api/patient/register`,
354
+ {
355
+ Data: {
356
+ TenantID: this.config.tenantID,
357
+ Credentials: {
358
+ Email: (_a2 = options.email) != null ? _a2 : "",
359
+ Password: (_b = options.password) != null ? _b : "",
360
+ TenantID: this.config.tenantID,
361
+ ...options.language ? { Language: options.language } : {}
362
+ },
363
+ User: {
364
+ FirstName: (_c = options.firstName) != null ? _c : "Unknown",
365
+ LastName: (_d = options.lastName) != null ? _d : "Unknown",
366
+ ...options.email ? { Email: options.email } : {},
367
+ ...options.phone ? { Phone: options.phone } : {},
368
+ ...options.birthDate ? { BirthDate: options.birthDate } : {},
369
+ ...options.gender ? { Gender: options.gender } : {},
370
+ ...options.middleName ? { MiddleName: options.middleName } : {},
371
+ ...options.race ? { Race: options.race } : {},
372
+ ...options.ethnicity ? { Ethnicity: options.ethnicity } : {},
373
+ ...options.sex ? { Sex: options.sex } : {},
374
+ ...options.genderIdentity ? { GenderIdentity: options.genderIdentity } : {},
375
+ ...options.status !== void 0 ? { Status: options.status } : {},
376
+ ...options.address ? { Address: options.address } : {},
377
+ Attributes: attributes
378
+ }
379
+ }
380
+ },
381
+ this.headers()
382
+ );
383
+ }
384
+ );
385
+ }
386
+ // =========================================================================
387
+ // Onboarding
388
+ // =========================================================================
389
+ async verifyEmail(email, code, language = "en", options = { verifyEmailCode: false }) {
390
+ return this.submitOnboardingStep(
391
+ "EMAIL_VERIFY",
392
+ this.buildEmailVerificationInput(email, options),
393
+ code,
394
+ language
395
+ );
396
+ }
397
+ async resendEmailVerify(email, language = "en", options = { verifyEmailCode: false }) {
398
+ return this.submitOnboardingStep(
399
+ "RESEND_EMAIL_VERIFY",
400
+ this.buildEmailVerificationInput(email, options),
401
+ "",
402
+ language
403
+ );
404
+ }
405
+ async resendSmsVerify(email, phone, language = "en") {
406
+ return this.submitOnboardingStep(
407
+ "RESEND_SMS_VERIFY",
408
+ { Email: email, Phone: phone },
409
+ "",
410
+ language
411
+ );
412
+ }
413
+ async verifySms(email, phone, code, language = "en") {
414
+ return this.submitOnboardingStep(
415
+ "SMS_VERIFY",
416
+ { Email: email, Phone: phone },
417
+ code,
418
+ language
419
+ );
420
+ }
421
+ async saveHealthProfile(profile, language = "en") {
422
+ return this.submitOnboardingStep(
423
+ "SAVE_HEALTH_PROFILE",
424
+ profile,
425
+ "",
426
+ language
427
+ );
428
+ }
429
+ async saveAddress(email, address, language = "en") {
430
+ return this.submitOnboardingStep(
431
+ "SAVE_ADDRESS",
432
+ { Email: email, Address: address },
433
+ "",
434
+ language
435
+ );
436
+ }
437
+ async submitOnboardingStep(step, user, data = "", language = "en") {
438
+ this.ensureConfigured();
439
+ const payload = {
440
+ Data: {
441
+ Data: data,
442
+ Step: step,
443
+ User: this.buildOnboardingUser(step, user),
444
+ Language: language
445
+ }
446
+ };
447
+ return this.execute(
448
+ `submitOnboardingStep(${step})`,
449
+ () => this.http.post(
450
+ `${this.config.baseUrl}/api/patient/onboard`,
451
+ payload,
452
+ this.headers()
453
+ )
454
+ );
455
+ }
456
+ // =========================================================================
457
+ // Password reset
458
+ // =========================================================================
459
+ async resetPassword(email, isPasswordResetWithOTP = false) {
460
+ return this.requestPasswordReset(email, isPasswordResetWithOTP);
461
+ }
462
+ async resetPasswordConfirm(payload) {
463
+ this.ensureConfigured();
464
+ return this.execute(
465
+ "resetPasswordConfirm",
466
+ () => this.http.post(
467
+ `${this.config.baseUrl}/api/patient/password`,
468
+ {
469
+ Data: {
470
+ ...payload.Data,
471
+ TenantID: this.config.tenantID
472
+ }
473
+ },
474
+ this.headers()
475
+ )
476
+ );
477
+ }
478
+ // =========================================================================
339
479
  // Private
340
480
  // =========================================================================
341
481
  async execute(operation, request) {
@@ -346,22 +486,6 @@ var HCLoginClient = class {
346
486
  if (err instanceof import_hc_http.APIError) {
347
487
  throw err;
348
488
  }
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
489
  if (err instanceof Error) {
366
490
  throw new import_hc_http.APIError({
367
491
  message: `${operation}: ${err.message}`,
@@ -390,17 +514,14 @@ var HCLoginClient = class {
390
514
  });
391
515
  }
392
516
  if (!response.IsOK) {
393
- throw this.mapBackendError(
394
- operation,
395
- response
396
- );
517
+ throw this.mapBackendError(operation, response);
397
518
  }
398
519
  return response;
399
520
  }
400
521
  mapBackendError(operation, response) {
401
522
  return new import_hc_http.HCServiceError(
402
523
  operation,
403
- response.ErrorMessage || "Unknown backend error",
524
+ response.ErrorMessage || "Unknown error",
404
525
  response
405
526
  );
406
527
  }
@@ -433,20 +554,6 @@ var HCLoginClient = class {
433
554
  [this.apiKeyHeaderName]: this.apiKeyValue
434
555
  };
435
556
  }
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
557
  ensureConfigured() {
451
558
  if (!this.config) {
452
559
  throw new import_hc_http.ConfigError(
@@ -475,6 +582,69 @@ var HCLoginClient = class {
475
582
  }
476
583
  return (_a = raw.ExpiresIn) != null ? _a : 0;
477
584
  }
585
+ buildOnboardingUser(step, user) {
586
+ var _a;
587
+ switch (step) {
588
+ case "EMAIL_VERIFY":
589
+ case "RESEND_EMAIL_VERIFY":
590
+ return {
591
+ Email: user.Email,
592
+ TenantID: this.config.tenantID,
593
+ ...user.Attributes ? { Attributes: user.Attributes } : {}
594
+ };
595
+ case "RESEND_SMS_VERIFY":
596
+ case "SMS_VERIFY":
597
+ return {
598
+ Email: user.Email,
599
+ TenantID: this.config.tenantID,
600
+ Phone: user.Phone
601
+ };
602
+ case "SAVE_HEALTH_PROFILE":
603
+ return {
604
+ Email: user.Email,
605
+ TenantID: this.config.tenantID,
606
+ FirstName: user.FirstName,
607
+ LastName: user.LastName,
608
+ BirthDate: user.BirthDate,
609
+ Gender: user.Gender,
610
+ Sex: user.Sex,
611
+ Status: (_a = user.Status) != null ? _a : 0,
612
+ Phone: user.Phone,
613
+ ...user.Race !== void 0 ? { Race: user.Race } : {},
614
+ ...user.Ethnicity !== void 0 ? { Ethnicity: user.Ethnicity } : {}
615
+ };
616
+ case "SAVE_ADDRESS":
617
+ return {
618
+ Email: user.Email,
619
+ TenantID: this.config.tenantID,
620
+ Address: user.Address ? {
621
+ StreetAndNumber: user.Address.StreetAndNumber,
622
+ Extension: user.Address.Extension,
623
+ City: user.Address.City,
624
+ State: user.Address.State,
625
+ PostalCode: user.Address.PostalCode,
626
+ Country: user.Address.Country
627
+ } : void 0
628
+ };
629
+ }
630
+ }
631
+ buildEmailVerificationInput(email, options) {
632
+ const attributes = this.buildEmailVerificationAttributes(options);
633
+ return {
634
+ Email: email,
635
+ ...attributes ? { Attributes: attributes } : {}
636
+ };
637
+ }
638
+ buildEmailVerificationAttributes(options) {
639
+ var _a;
640
+ const attributes = {
641
+ ...(_a = options.attributes) != null ? _a : {}
642
+ };
643
+ if (options.verifyEmailCode === true) {
644
+ attributes.VERIFY_EMAIL_CODE = "true";
645
+ }
646
+ return Object.keys(attributes).length > 0 ? attributes : void 0;
647
+ }
478
648
  };
479
649
 
480
650
  // src/errors.ts