@dloizides/auth-web 1.23.0 → 1.24.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.js CHANGED
@@ -398,6 +398,45 @@ var DEFAULT_VERIFY_EMAIL_LABELS = {
398
398
  signIn: DEFAULT_VERIFY_SIGN_IN_LABELS
399
399
  };
400
400
 
401
+ // src/components/labels/accountDeletionLabels.ts
402
+ var DEFAULT_ACCOUNT_DELETION_LABELS = {
403
+ title: "Delete your account",
404
+ subtitle: "We email you a link to confirm. Nothing is deleted until you open it.",
405
+ reason: {
406
+ label: "Reason (optional)",
407
+ placeholder: "Why are you leaving?",
408
+ hint: "Optional reason for deleting your account",
409
+ inputLabel: "Reason for deleting your account"
410
+ },
411
+ submit: "Request account deletion",
412
+ submitHint: "Emails you a link to confirm the deletion",
413
+ submitting: "Sending the request",
414
+ stepUpTitle: "Confirm your password",
415
+ stepUpSubtitle: "You signed in a while ago. Enter your password to continue.",
416
+ password: {
417
+ label: "Password",
418
+ placeholder: "Your password",
419
+ hint: "Your current password",
420
+ inputLabel: "Password"
421
+ },
422
+ stepUpSubmit: "Confirm and continue",
423
+ stepUpSubmitHint: "Checks your password, then sends the deletion request",
424
+ checkEmailTitle: "Check your email",
425
+ checkEmailBody: "Open the link we sent you to confirm. Your account stays active until then.",
426
+ emailNotSentTitle: "We could not send the email",
427
+ emailNotSentBody: "Your request is recorded, but the confirmation email did not go out. Contact support to finish it.",
428
+ scheduledDate: "Scheduled deletion date: {date}",
429
+ alreadyRequestedTitle: "Deletion already requested",
430
+ alreadyRequestedBody: "Look for the confirmation link in your email. You do not need to ask again.",
431
+ errorPasswordRequired: "Enter your password.",
432
+ errorWrongPassword: "That password is not correct.",
433
+ errorSignInRequired: "Your session ended. Sign in and try again.",
434
+ errorRateLimited: "Too many attempts. Wait a moment and try again.",
435
+ errorRateLimitedSeconds: "Too many attempts. Try again in {seconds} seconds.",
436
+ errorRetryLater: "We could not reach the account service. Try again later.",
437
+ errorFailed: "Something went wrong. Try again."
438
+ };
439
+
401
440
  // src/components/testIds.ts
402
441
  var AuthTestIds = {
403
442
  loginForm: "auth-login-form",
@@ -680,7 +719,7 @@ function useLoginSubmit({
680
719
  missingFieldsLabel,
681
720
  invalidCredentialsLabel
682
721
  }) {
683
- const [errorText, setErrorText] = react.useState(null);
722
+ const [errorText2, setErrorText] = react.useState(null);
684
723
  const inFlight = react.useRef(false);
685
724
  const runLogin = react.useCallback(async () => {
686
725
  if (inFlight.current) {
@@ -706,7 +745,7 @@ function useLoginSubmit({
706
745
  const handleSubmit = react.useCallback(() => {
707
746
  void runLogin();
708
747
  }, [runLogin]);
709
- return { errorText, handleSubmit };
748
+ return { errorText: errorText2, handleSubmit };
710
749
  }
711
750
  function isKeyRepeat(event) {
712
751
  const native = event?.nativeEvent;
@@ -866,7 +905,7 @@ function LoginForm({
866
905
  prefillNonce,
867
906
  prefillIntent
868
907
  });
869
- const { errorText, handleSubmit } = useLoginSubmit({
908
+ const { errorText: errorText2, handleSubmit } = useLoginSubmit({
870
909
  username,
871
910
  password,
872
911
  login,
@@ -922,12 +961,12 @@ function LoginForm({
922
961
  }
923
962
  )
924
963
  ] }),
925
- errorText !== null ? /* @__PURE__ */ jsxRuntime.jsx(
964
+ errorText2 !== null ? /* @__PURE__ */ jsxRuntime.jsx(
926
965
  reactNative.Text,
927
966
  {
928
967
  style: styles3.errorText,
929
968
  testID: withTestIdPrefix(AuthTestIds.loginError, testIdPrefix),
930
- children: errorText
969
+ children: errorText2
931
970
  }
932
971
  ) : null,
933
972
  onForgotPassword !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -1019,7 +1058,7 @@ function ForgotPasswordForm({
1019
1058
  );
1020
1059
  const [email, setEmail] = react.useState("");
1021
1060
  const [submitted, setSubmitted] = react.useState(false);
1022
- const [errorText, setErrorText] = react.useState(null);
1061
+ const [errorText2, setErrorText] = react.useState(null);
1023
1062
  const mutationOptions = react.useMemo(
1024
1063
  () => ({
1025
1064
  client,
@@ -1081,12 +1120,12 @@ function ForgotPasswordForm({
1081
1120
  }
1082
1121
  )
1083
1122
  ] }),
1084
- errorText !== null ? /* @__PURE__ */ jsxRuntime.jsx(
1123
+ errorText2 !== null ? /* @__PURE__ */ jsxRuntime.jsx(
1085
1124
  reactNative.Text,
1086
1125
  {
1087
1126
  style: styles3.errorText,
1088
1127
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordError, testIdPrefix),
1089
- children: errorText
1128
+ children: errorText2
1090
1129
  }
1091
1130
  ) : null,
1092
1131
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -1574,7 +1613,7 @@ function OtpRequestStep({
1574
1613
  labels,
1575
1614
  testIdPrefix,
1576
1615
  isSubmitting,
1577
- errorText,
1616
+ errorText: errorText2,
1578
1617
  onSubmit
1579
1618
  }) {
1580
1619
  const [email, setEmail] = react.useState("");
@@ -1602,7 +1641,7 @@ function OtpRequestStep({
1602
1641
  }
1603
1642
  )
1604
1643
  ] }),
1605
- errorText !== null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText }) : null,
1644
+ errorText2 !== null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText2 }) : null,
1606
1645
  /* @__PURE__ */ jsxRuntime.jsx(
1607
1646
  reactNative.TouchableOpacity,
1608
1647
  {
@@ -1625,7 +1664,7 @@ function OtpVerifyStep({
1625
1664
  testIdPrefix,
1626
1665
  identifier,
1627
1666
  isSubmitting,
1628
- errorText,
1667
+ errorText: errorText2,
1629
1668
  onSubmit,
1630
1669
  onResend,
1631
1670
  onChangeEmail
@@ -1656,7 +1695,7 @@ function OtpVerifyStep({
1656
1695
  }
1657
1696
  )
1658
1697
  ] }),
1659
- errorText !== null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText }) : null,
1698
+ errorText2 !== null ? /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText2 }) : null,
1660
1699
  /* @__PURE__ */ jsxRuntime.jsx(
1661
1700
  reactNative.TouchableOpacity,
1662
1701
  {
@@ -1879,11 +1918,11 @@ function OtpForm({
1879
1918
  setLocalError(null);
1880
1919
  otp.reset();
1881
1920
  }, [otp]);
1882
- const errorText = localError ?? transportErrorFor(otp.step, otp.error, labels);
1921
+ const errorText2 = localError ?? transportErrorFor(otp.step, otp.error, labels);
1883
1922
  const content = otp.step === "requestCode" /* RequestCode */ ? /* @__PURE__ */ jsxRuntime.jsx(
1884
1923
  OtpRequestStep,
1885
1924
  {
1886
- errorText,
1925
+ errorText: errorText2,
1887
1926
  isSubmitting: otp.isSubmitting,
1888
1927
  labels,
1889
1928
  styles: styles3,
@@ -1894,7 +1933,7 @@ function OtpForm({
1894
1933
  ) : /* @__PURE__ */ jsxRuntime.jsx(
1895
1934
  OtpVerifyStep,
1896
1935
  {
1897
- errorText,
1936
+ errorText: errorText2,
1898
1937
  identifier: otp.identifier,
1899
1938
  isSubmitting: otp.isSubmitting,
1900
1939
  labels,
@@ -2006,7 +2045,7 @@ function PinForm({
2006
2045
  const [localError, setLocalError] = react.useState(null);
2007
2046
  const [pinValue, setPinValue] = react.useState("");
2008
2047
  const handleSubmit = useSubmitHandler(pin, labels, setLocalError, onSuccess);
2009
- const errorText = localError ?? transportErrorFor2(pin.error, labels);
2048
+ const errorText2 = localError ?? transportErrorFor2(pin.error, labels);
2010
2049
  const buttonStyle = pin.isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
2011
2050
  const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2012
2051
  /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.title, children: labels.title }),
@@ -2032,12 +2071,12 @@ function PinForm({
2032
2071
  }
2033
2072
  )
2034
2073
  ] }),
2035
- errorText !== null ? /* @__PURE__ */ jsxRuntime.jsx(
2074
+ errorText2 !== null ? /* @__PURE__ */ jsxRuntime.jsx(
2036
2075
  reactNative.Text,
2037
2076
  {
2038
2077
  style: styles3.errorText,
2039
2078
  testID: withTestIdPrefix(AuthTestIds.pinError, testIdPrefix),
2040
- children: errorText
2079
+ children: errorText2
2041
2080
  }
2042
2081
  ) : null,
2043
2082
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2263,7 +2302,7 @@ function useDevicePinUnlock({
2263
2302
  const [pin, setPinState] = react.useState("");
2264
2303
  const [submitting, setSubmitting] = react.useState(false);
2265
2304
  const [errorKey, setErrorKey] = react.useState(null);
2266
- const [retryAfterSeconds, setRetryAfterSeconds] = react.useState(null);
2305
+ const [retryAfterSeconds2, setRetryAfterSeconds] = react.useState(null);
2267
2306
  const setPin = react.useCallback((next) => {
2268
2307
  setPinState(next);
2269
2308
  setErrorKey(null);
@@ -2303,7 +2342,7 @@ function useDevicePinUnlock({
2303
2342
  setSubmitting(false);
2304
2343
  });
2305
2344
  }, [client, pin, digits, onSignedIn]);
2306
- return { pin, submitting, errorKey, retryAfterSeconds, setPin, submit };
2345
+ return { pin, submitting, errorKey, retryAfterSeconds: retryAfterSeconds2, setPin, submit };
2307
2346
  }
2308
2347
  function resolveErrorText(errorKey, digits, retryAfter, labels) {
2309
2348
  if (errorKey === null) {
@@ -2339,14 +2378,14 @@ function DevicePinUnlockScreen({
2339
2378
  () => ({ ...DEFAULT_DEVICE_PIN_UNLOCK_LABELS, ...labelsProp }),
2340
2379
  [labelsProp]
2341
2380
  );
2342
- const { pin, submitting, errorKey, retryAfterSeconds, setPin, submit } = useDevicePinUnlock({
2381
+ const { pin, submitting, errorKey, retryAfterSeconds: retryAfterSeconds2, setPin, submit } = useDevicePinUnlock({
2343
2382
  client,
2344
2383
  digits,
2345
2384
  onSignedIn
2346
2385
  });
2347
2386
  const hasName = rememberedUsername !== "";
2348
2387
  const title = hasName ? interpolate(labels.title, { name: rememberedUsername }) : labels.titleNoName;
2349
- const error = resolveErrorText(errorKey, digits, retryAfterSeconds, labels);
2388
+ const error = resolveErrorText(errorKey, digits, retryAfterSeconds2, labels);
2350
2389
  const buttonStyle = submitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
2351
2390
  return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: /* @__PURE__ */ jsxRuntime.jsxs(
2352
2391
  reactNative.View,
@@ -4306,13 +4345,13 @@ var RegisterErrorCode = /* @__PURE__ */ ((RegisterErrorCode2) => {
4306
4345
  })(RegisterErrorCode || {});
4307
4346
 
4308
4347
  // src/hooks/routeRegisterOutcome.ts
4309
- function routeRegisterOutcome(outcome, handlers) {
4310
- if (outcome.ok) {
4311
- handlers.onSuccess(outcome.result);
4348
+ function routeRegisterOutcome(outcome2, handlers) {
4349
+ if (outcome2.ok) {
4350
+ handlers.onSuccess(outcome2.result);
4312
4351
  return;
4313
4352
  }
4314
- if (outcome.error !== "inFlight" /* InFlight */) {
4315
- handlers.onError?.(outcome.error);
4353
+ if (outcome2.error !== "inFlight" /* InFlight */) {
4354
+ handlers.onError?.(outcome2.error);
4316
4355
  }
4317
4356
  }
4318
4357
 
@@ -5442,6 +5481,432 @@ function SharedRegisterForm({
5442
5481
  return /* @__PURE__ */ jsxRuntime.jsx(RegisterFormCore, { ...formProps, sharedRegistration: true, onSuccess: handleResult });
5443
5482
  }
5444
5483
 
5484
+ // src/account/AccountDeletionStatus.ts
5485
+ var AccountDeletionStatus = /* @__PURE__ */ ((AccountDeletionStatus2) => {
5486
+ AccountDeletionStatus2["Accepted"] = "accepted";
5487
+ AccountDeletionStatus2["StepUpRequired"] = "stepUpRequired";
5488
+ AccountDeletionStatus2["AlreadyRequested"] = "alreadyRequested";
5489
+ AccountDeletionStatus2["SignInRequired"] = "signInRequired";
5490
+ AccountDeletionStatus2["RateLimited"] = "rateLimited";
5491
+ AccountDeletionStatus2["RetryLater"] = "retryLater";
5492
+ AccountDeletionStatus2["Failed"] = "failed";
5493
+ return AccountDeletionStatus2;
5494
+ })(AccountDeletionStatus || {});
5495
+
5496
+ // src/account/StepUpStatus.ts
5497
+ var StepUpStatus = /* @__PURE__ */ ((StepUpStatus2) => {
5498
+ StepUpStatus2["Confirmed"] = "confirmed";
5499
+ StepUpStatus2["WrongPassword"] = "wrongPassword";
5500
+ StepUpStatus2["RateLimited"] = "rateLimited";
5501
+ StepUpStatus2["Failed"] = "failed";
5502
+ return StepUpStatus2;
5503
+ })(StepUpStatus || {});
5504
+
5505
+ // src/account/accountDeletionClient.ts
5506
+ var ACCOUNT_DELETE_REQUEST_ENDPOINT = "/bff/account/delete-request";
5507
+ var STEP_UP_PASSWORD_ENDPOINT = "/bff/step-up/password";
5508
+ var RECENT_AUTHENTICATION_REQUIRED = "RECENT_AUTHENTICATION_REQUIRED";
5509
+ var HTTP_ACCEPTED2 = 202;
5510
+ var HTTP_UNAUTHORIZED3 = 401;
5511
+ var HTTP_FORBIDDEN = 403;
5512
+ var HTTP_CONFLICT = 409;
5513
+ var HTTP_TOO_MANY_REQUESTS3 = 429;
5514
+ var HTTP_BAD_GATEWAY = 502;
5515
+ var DELTA_SECONDS = /^\d+$/;
5516
+ function retryAfterSeconds(response) {
5517
+ const raw = response.header?.("Retry-After")?.trim();
5518
+ return raw !== void 0 && DELTA_SECONDS.test(raw) ? Number(raw) : null;
5519
+ }
5520
+ function decodeReceipt(data) {
5521
+ const body = isRecord3(data) ? data : {};
5522
+ return {
5523
+ requestId: typeof body.requestId === "string" ? body.requestId : "",
5524
+ scheduledDeletionDate: typeof body.scheduledDeletionDate === "string" ? body.scheduledDeletionDate : "",
5525
+ confirmationEmailSent: body.confirmationEmailSent === true
5526
+ };
5527
+ }
5528
+ function isStaleLogin(data) {
5529
+ return isRecord3(data) && data.code === RECENT_AUTHENTICATION_REQUIRED;
5530
+ }
5531
+ function decodeDeletion(response) {
5532
+ switch (response.status) {
5533
+ case HTTP_ACCEPTED2:
5534
+ return { status: "accepted" /* Accepted */, receipt: decodeReceipt(response.data) };
5535
+ case HTTP_FORBIDDEN:
5536
+ return { status: isStaleLogin(response.data) ? "stepUpRequired" /* StepUpRequired */ : "failed" /* Failed */ };
5537
+ case HTTP_CONFLICT:
5538
+ return { status: "alreadyRequested" /* AlreadyRequested */ };
5539
+ case HTTP_UNAUTHORIZED3:
5540
+ return { status: "signInRequired" /* SignInRequired */ };
5541
+ case HTTP_TOO_MANY_REQUESTS3:
5542
+ return { status: "rateLimited" /* RateLimited */, retryAfterSeconds: retryAfterSeconds(response) };
5543
+ case HTTP_BAD_GATEWAY:
5544
+ return { status: "retryLater" /* RetryLater */ };
5545
+ default:
5546
+ return { status: "failed" /* Failed */ };
5547
+ }
5548
+ }
5549
+ function decodeStepUp(response) {
5550
+ if (response.ok) {
5551
+ return { status: "confirmed" /* Confirmed */ };
5552
+ }
5553
+ if (response.status === HTTP_UNAUTHORIZED3) {
5554
+ return { status: "wrongPassword" /* WrongPassword */ };
5555
+ }
5556
+ if (response.status === HTTP_TOO_MANY_REQUESTS3) {
5557
+ return { status: "rateLimited" /* RateLimited */, retryAfterSeconds: retryAfterSeconds(response) };
5558
+ }
5559
+ return { status: "failed" /* Failed */ };
5560
+ }
5561
+ function createBffAccountDeletionClient(options = {}) {
5562
+ const http = options.http ?? lazyFetchHttpClient3;
5563
+ const baseUrl = options.baseUrl ?? "";
5564
+ const post = (path, body) => http({
5565
+ url: `${baseUrl}${path}`,
5566
+ method: "POST",
5567
+ headers: { "Content-Type": "application/json", "X-BFF-Csrf": "1" },
5568
+ body: JSON.stringify(body),
5569
+ credentials: "include"
5570
+ });
5571
+ return {
5572
+ requestDeletion: async (reason) => {
5573
+ try {
5574
+ return decodeDeletion(await post(ACCOUNT_DELETE_REQUEST_ENDPOINT, reason === void 0 ? {} : { reason }));
5575
+ } catch {
5576
+ return { status: "failed" /* Failed */ };
5577
+ }
5578
+ },
5579
+ stepUpPassword: async (password) => {
5580
+ try {
5581
+ return decodeStepUp(await post(STEP_UP_PASSWORD_ENDPOINT, { password }));
5582
+ } catch {
5583
+ return { status: "failed" /* Failed */ };
5584
+ }
5585
+ }
5586
+ };
5587
+ }
5588
+
5589
+ // src/account/AccountDeletionErrorKey.ts
5590
+ var AccountDeletionErrorKey = /* @__PURE__ */ ((AccountDeletionErrorKey2) => {
5591
+ AccountDeletionErrorKey2["PasswordRequired"] = "passwordRequired";
5592
+ AccountDeletionErrorKey2["WrongPassword"] = "wrongPassword";
5593
+ AccountDeletionErrorKey2["SignInRequired"] = "signInRequired";
5594
+ AccountDeletionErrorKey2["RateLimited"] = "rateLimited";
5595
+ AccountDeletionErrorKey2["RetryLater"] = "retryLater";
5596
+ AccountDeletionErrorKey2["Failed"] = "failed";
5597
+ return AccountDeletionErrorKey2;
5598
+ })(AccountDeletionErrorKey || {});
5599
+
5600
+ // src/account/AccountDeletionPhase.ts
5601
+ var AccountDeletionPhase = /* @__PURE__ */ ((AccountDeletionPhase2) => {
5602
+ AccountDeletionPhase2["Confirm"] = "confirm";
5603
+ AccountDeletionPhase2["StepUp"] = "stepUp";
5604
+ AccountDeletionPhase2["Accepted"] = "accepted";
5605
+ AccountDeletionPhase2["AlreadyRequested"] = "alreadyRequested";
5606
+ return AccountDeletionPhase2;
5607
+ })(AccountDeletionPhase || {});
5608
+
5609
+ // src/account/accountDeletionFlow.ts
5610
+ function outcome(phase, errorKey = null, retryAfterSeconds2 = null) {
5611
+ return { phase, errorKey, receipt: null, retryAfterSeconds: retryAfterSeconds2 };
5612
+ }
5613
+ function fromDeletion(result, steppedUp) {
5614
+ switch (result.status) {
5615
+ case "accepted" /* Accepted */:
5616
+ return { ...outcome("accepted" /* Accepted */), receipt: result.receipt };
5617
+ case "stepUpRequired" /* StepUpRequired */:
5618
+ return outcome("stepUp" /* StepUp */, steppedUp ? "failed" /* Failed */ : null);
5619
+ case "alreadyRequested" /* AlreadyRequested */:
5620
+ return outcome("alreadyRequested" /* AlreadyRequested */);
5621
+ case "signInRequired" /* SignInRequired */:
5622
+ return outcome("confirm" /* Confirm */, "signInRequired" /* SignInRequired */);
5623
+ case "rateLimited" /* RateLimited */:
5624
+ return outcome("confirm" /* Confirm */, "rateLimited" /* RateLimited */, result.retryAfterSeconds);
5625
+ case "retryLater" /* RetryLater */:
5626
+ return outcome("confirm" /* Confirm */, "retryLater" /* RetryLater */);
5627
+ default:
5628
+ return outcome("confirm" /* Confirm */, "failed" /* Failed */);
5629
+ }
5630
+ }
5631
+ function fromStepUpFailure(result) {
5632
+ if (result.status === "wrongPassword" /* WrongPassword */) {
5633
+ return outcome("stepUp" /* StepUp */, "wrongPassword" /* WrongPassword */);
5634
+ }
5635
+ if (result.status === "rateLimited" /* RateLimited */) {
5636
+ return outcome("stepUp" /* StepUp */, "rateLimited" /* RateLimited */, result.retryAfterSeconds);
5637
+ }
5638
+ return outcome("stepUp" /* StepUp */, "failed" /* Failed */);
5639
+ }
5640
+ async function runDeletionAttempt(client, { phase, reason, password }) {
5641
+ const trimmed = reason.trim();
5642
+ const sentReason = trimmed === "" ? void 0 : trimmed;
5643
+ if (phase !== "stepUp" /* StepUp */) {
5644
+ return fromDeletion(await client.requestDeletion(sentReason), false);
5645
+ }
5646
+ const stepUp = await client.stepUpPassword(password);
5647
+ if (stepUp.status !== "confirmed" /* Confirmed */) {
5648
+ return fromStepUpFailure(stepUp);
5649
+ }
5650
+ return fromDeletion(await client.requestDeletion(sentReason), true);
5651
+ }
5652
+ var MS_PER_SECOND = 1e3;
5653
+ function useRetryAfterBlock(onExpired) {
5654
+ const [retryAfterSeconds2, setRetryAfterSeconds] = react.useState(null);
5655
+ const timer = react.useRef(null);
5656
+ const stopTimer = react.useCallback(() => {
5657
+ if (timer.current !== null) {
5658
+ clearTimeout(timer.current);
5659
+ }
5660
+ timer.current = null;
5661
+ }, []);
5662
+ react.useEffect(() => stopTimer, [stopTimer]);
5663
+ const block = react.useCallback(
5664
+ (seconds) => {
5665
+ stopTimer();
5666
+ setRetryAfterSeconds(seconds);
5667
+ timer.current = setTimeout(() => {
5668
+ timer.current = null;
5669
+ setRetryAfterSeconds(null);
5670
+ onExpired();
5671
+ }, seconds * MS_PER_SECOND);
5672
+ },
5673
+ [onExpired, stopTimer]
5674
+ );
5675
+ return { retryAfterSeconds: retryAfterSeconds2, block };
5676
+ }
5677
+
5678
+ // src/account/useAccountDeletion.ts
5679
+ function useAccountDeletion(args) {
5680
+ const { client, onAccepted, onSignInRequired } = args;
5681
+ const [reason, setReason] = react.useState("");
5682
+ const [password, setPasswordValue] = react.useState("");
5683
+ const [phase, setPhase] = react.useState("confirm" /* Confirm */);
5684
+ const [submitting, setSubmitting] = react.useState(false);
5685
+ const [errorKey, setErrorKey] = react.useState(null);
5686
+ const [receipt, setReceipt] = react.useState(null);
5687
+ const inFlight = react.useRef(false);
5688
+ const clearRateLimit = react.useCallback(() => {
5689
+ setErrorKey((current) => current === "rateLimited" /* RateLimited */ ? null : current);
5690
+ }, []);
5691
+ const { retryAfterSeconds: retryAfterSeconds2, block } = useRetryAfterBlock(clearRateLimit);
5692
+ const blocked = retryAfterSeconds2 !== null;
5693
+ const setPassword = react.useCallback((value) => {
5694
+ setPasswordValue(value);
5695
+ setErrorKey((current) => current === "rateLimited" /* RateLimited */ ? current : null);
5696
+ }, []);
5697
+ const applyOutcome = react.useCallback((outcome2) => {
5698
+ setPhase(outcome2.phase);
5699
+ setErrorKey(outcome2.errorKey);
5700
+ setPasswordValue("");
5701
+ if (outcome2.retryAfterSeconds !== null) {
5702
+ block(outcome2.retryAfterSeconds);
5703
+ }
5704
+ if (outcome2.errorKey === "signInRequired" /* SignInRequired */) {
5705
+ onSignInRequired?.();
5706
+ }
5707
+ if (outcome2.receipt === null) {
5708
+ return;
5709
+ }
5710
+ setReceipt(outcome2.receipt);
5711
+ onAccepted?.(outcome2.receipt);
5712
+ }, [block, onAccepted, onSignInRequired]);
5713
+ const submit = react.useCallback(() => {
5714
+ if (inFlight.current || blocked) {
5715
+ return;
5716
+ }
5717
+ if (phase === "stepUp" /* StepUp */ && password === "") {
5718
+ setErrorKey("passwordRequired" /* PasswordRequired */);
5719
+ return;
5720
+ }
5721
+ inFlight.current = true;
5722
+ setSubmitting(true);
5723
+ setErrorKey(null);
5724
+ runDeletionAttempt(client, { phase, reason, password }).then(applyOutcome).catch(() => {
5725
+ setPasswordValue("");
5726
+ setErrorKey("failed" /* Failed */);
5727
+ }).finally(() => {
5728
+ inFlight.current = false;
5729
+ setSubmitting(false);
5730
+ });
5731
+ }, [applyOutcome, blocked, client, password, phase, reason]);
5732
+ return { phase, reason, password, setReason, setPassword, submitting, blocked, retryAfterSeconds: retryAfterSeconds2, errorKey, receipt, submit };
5733
+ }
5734
+ var REASON_MAX_LENGTH = 1e3;
5735
+ function AccountDeletionField({
5736
+ copy,
5737
+ placeholderColor,
5738
+ secure,
5739
+ disabled,
5740
+ value,
5741
+ styles: styles3,
5742
+ testID,
5743
+ onChangeText,
5744
+ onSubmitEditing
5745
+ }) {
5746
+ const inputRef = react.useRef(null);
5747
+ useFocusOnMount(inputRef);
5748
+ return /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.fieldGroup, children: [
5749
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.label, children: copy.label }),
5750
+ /* @__PURE__ */ jsxRuntime.jsx(
5751
+ reactNative.TextInput,
5752
+ {
5753
+ ref: inputRef,
5754
+ accessibilityHint: copy.hint,
5755
+ accessibilityLabel: copy.inputLabel,
5756
+ autoComplete: secure ? "current-password" : "off",
5757
+ editable: !disabled,
5758
+ maxLength: secure ? void 0 : REASON_MAX_LENGTH,
5759
+ placeholder: copy.placeholder,
5760
+ placeholderTextColor: placeholderColor,
5761
+ returnKeyType: "go",
5762
+ secureTextEntry: secure,
5763
+ style: styles3.input,
5764
+ testID,
5765
+ value,
5766
+ onChangeText,
5767
+ onSubmitEditing
5768
+ }
5769
+ )
5770
+ ] });
5771
+ }
5772
+ function AccountDeletionNotice({
5773
+ title,
5774
+ body,
5775
+ detail,
5776
+ styles: styles3,
5777
+ testID,
5778
+ footer
5779
+ }) {
5780
+ const headingRef = react.useRef(null);
5781
+ useFocusOnMount(headingRef);
5782
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { accessibilityLiveRegion: "polite", style: styles3.card, testID, children: [
5783
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { ref: headingRef, accessible: true, focusable: true, children: /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { accessibilityRole: "header", style: styles3.title, children: title }) }),
5784
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.subtitle, children: body }),
5785
+ detail === null ? null : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.helperText, children: detail }),
5786
+ footer
5787
+ ] }) });
5788
+ }
5789
+
5790
+ // src/components/accountDeletionTestIds.ts
5791
+ var AccountDeletionTestIds = {
5792
+ form: "account-deletion-form",
5793
+ reasonInput: "account-deletion-reason-input",
5794
+ stepUpInput: "account-deletion-password-input",
5795
+ submitButton: "account-deletion-submit-button",
5796
+ error: "account-deletion-error",
5797
+ checkEmail: "account-deletion-check-email",
5798
+ emailNotSent: "account-deletion-email-not-sent",
5799
+ alreadyRequested: "account-deletion-already-requested"
5800
+ };
5801
+ function errorText(errorKey, seconds, labels) {
5802
+ switch (errorKey) {
5803
+ case null:
5804
+ return null;
5805
+ case "passwordRequired" /* PasswordRequired */:
5806
+ return labels.errorPasswordRequired;
5807
+ case "wrongPassword" /* WrongPassword */:
5808
+ return labels.errorWrongPassword;
5809
+ case "signInRequired" /* SignInRequired */:
5810
+ return labels.errorSignInRequired;
5811
+ case "rateLimited" /* RateLimited */:
5812
+ return seconds === null ? labels.errorRateLimited : interpolate(labels.errorRateLimitedSeconds, { seconds });
5813
+ case "retryLater" /* RetryLater */:
5814
+ return labels.errorRetryLater;
5815
+ default:
5816
+ return labels.errorFailed;
5817
+ }
5818
+ }
5819
+ function scheduledDetail(receipt, labels) {
5820
+ const date = new Date(receipt?.scheduledDeletionDate ?? "");
5821
+ return Number.isNaN(date.getTime()) ? null : interpolate(labels.scheduledDate, { date: date.toLocaleDateString() });
5822
+ }
5823
+ function noticeCopy(phase, receipt, labels) {
5824
+ if (phase === "alreadyRequested" /* AlreadyRequested */) {
5825
+ return {
5826
+ title: labels.alreadyRequestedTitle,
5827
+ body: labels.alreadyRequestedBody,
5828
+ detail: null,
5829
+ testId: AccountDeletionTestIds.alreadyRequested
5830
+ };
5831
+ }
5832
+ if (phase !== "accepted" /* Accepted */) {
5833
+ return null;
5834
+ }
5835
+ const detail = scheduledDetail(receipt, labels);
5836
+ if (receipt?.confirmationEmailSent === true) {
5837
+ return { title: labels.checkEmailTitle, body: labels.checkEmailBody, detail, testId: AccountDeletionTestIds.checkEmail };
5838
+ }
5839
+ return { title: labels.emailNotSentTitle, body: labels.emailNotSentBody, detail, testId: AccountDeletionTestIds.emailNotSent };
5840
+ }
5841
+ function AccountDeletionForm({
5842
+ client,
5843
+ labels: labelsProp,
5844
+ theme: themeProp,
5845
+ testIdPrefix,
5846
+ footer,
5847
+ onAccepted,
5848
+ onSignInRequired
5849
+ }) {
5850
+ const theme = useAuthTheme(themeProp);
5851
+ const styles3 = useAuthStyles(theme);
5852
+ const labels = react.useMemo(() => ({ ...DEFAULT_ACCOUNT_DELETION_LABELS, ...labelsProp }), [labelsProp]);
5853
+ const form = useAccountDeletion({ client, onAccepted, onSignInRequired });
5854
+ const handleSubmitEditing = useSubmitOnEnter(form.submit);
5855
+ const testId = (id) => withTestIdPrefix(id, testIdPrefix);
5856
+ const notice = noticeCopy(form.phase, form.receipt, labels);
5857
+ if (notice !== null) {
5858
+ return /* @__PURE__ */ jsxRuntime.jsx(
5859
+ AccountDeletionNotice,
5860
+ {
5861
+ body: notice.body,
5862
+ detail: notice.detail,
5863
+ footer,
5864
+ styles: styles3,
5865
+ testID: testId(notice.testId),
5866
+ title: notice.title
5867
+ }
5868
+ );
5869
+ }
5870
+ const steppingUp = form.phase === "stepUp" /* StepUp */;
5871
+ const message = errorText(form.errorKey, form.retryAfterSeconds, labels);
5872
+ const submitLabel = steppingUp ? labels.stepUpSubmit : labels.submit;
5873
+ return /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.screen, children: /* @__PURE__ */ jsxRuntime.jsxs(reactNative.View, { style: styles3.card, testID: testId(AccountDeletionTestIds.form), children: [
5874
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.title, children: steppingUp ? labels.stepUpTitle : labels.title }),
5875
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { style: styles3.subtitle, children: steppingUp ? labels.stepUpSubtitle : labels.subtitle }),
5876
+ /* @__PURE__ */ jsxRuntime.jsx(
5877
+ AccountDeletionField,
5878
+ {
5879
+ copy: steppingUp ? labels.password : labels.reason,
5880
+ disabled: form.submitting,
5881
+ placeholderColor: theme.colors.textSecondary,
5882
+ secure: steppingUp,
5883
+ styles: styles3,
5884
+ testID: testId(steppingUp ? AccountDeletionTestIds.stepUpInput : AccountDeletionTestIds.reasonInput),
5885
+ value: steppingUp ? form.password : form.reason,
5886
+ onChangeText: steppingUp ? form.setPassword : form.setReason,
5887
+ onSubmitEditing: handleSubmitEditing
5888
+ },
5889
+ form.phase
5890
+ ),
5891
+ message === null ? null : /* @__PURE__ */ jsxRuntime.jsx(reactNative.Text, { accessibilityLiveRegion: "polite", style: styles3.errorText, testID: testId(AccountDeletionTestIds.error), children: message }),
5892
+ /* @__PURE__ */ jsxRuntime.jsx(reactNative.View, { style: styles3.fieldGroup, children: /* @__PURE__ */ jsxRuntime.jsx(
5893
+ uiButtons.Button,
5894
+ {
5895
+ fullWidth: true,
5896
+ accessibilityHint: steppingUp ? labels.stepUpSubmitHint : labels.submitHint,
5897
+ accessibilityLabel: form.submitting ? labels.submitting : submitLabel,
5898
+ disabled: form.blocked,
5899
+ label: submitLabel,
5900
+ loading: form.submitting,
5901
+ testID: testId(AccountDeletionTestIds.submitButton),
5902
+ variant: "danger",
5903
+ onPress: form.submit
5904
+ }
5905
+ ) }),
5906
+ footer
5907
+ ] }) });
5908
+ }
5909
+
5445
5910
  Object.defineProperty(exports, "BffAuthClient", {
5446
5911
  enumerable: true,
5447
5912
  get: function () { return authClient.BffAuthClient; }
@@ -5450,6 +5915,12 @@ Object.defineProperty(exports, "createFetchHttpClient", {
5450
5915
  enumerable: true,
5451
5916
  get: function () { return authClient.createFetchHttpClient; }
5452
5917
  });
5918
+ exports.ACCOUNT_DELETE_REQUEST_ENDPOINT = ACCOUNT_DELETE_REQUEST_ENDPOINT;
5919
+ exports.AccountDeletionErrorKey = AccountDeletionErrorKey;
5920
+ exports.AccountDeletionForm = AccountDeletionForm;
5921
+ exports.AccountDeletionPhase = AccountDeletionPhase;
5922
+ exports.AccountDeletionStatus = AccountDeletionStatus;
5923
+ exports.AccountDeletionTestIds = AccountDeletionTestIds;
5453
5924
  exports.AccountSettingsScreen = AccountSettingsScreen;
5454
5925
  exports.AuthScreen = AuthScreen;
5455
5926
  exports.AuthTestIds = AuthTestIds;
@@ -5458,6 +5929,7 @@ exports.BffAuthStatus = BffAuthStatus;
5458
5929
  exports.CHANGE_PASSWORD_ENDPOINT = CHANGE_PASSWORD_ENDPOINT;
5459
5930
  exports.ChangePasswordCard = ChangePasswordCard;
5460
5931
  exports.ChangePasswordErrorKey = ChangePasswordErrorKey;
5932
+ exports.DEFAULT_ACCOUNT_DELETION_LABELS = DEFAULT_ACCOUNT_DELETION_LABELS;
5461
5933
  exports.DEFAULT_ACCOUNT_SETTINGS_LABELS = DEFAULT_ACCOUNT_SETTINGS_LABELS;
5462
5934
  exports.DEFAULT_AUTH_CARD_MAX_WIDTH = DEFAULT_AUTH_CARD_MAX_WIDTH;
5463
5935
  exports.DEFAULT_DEMO_CREDENTIALS_HINT_LABELS = DEFAULT_DEMO_CREDENTIALS_HINT_LABELS;
@@ -5513,7 +5985,9 @@ exports.RegisterForm = RegisterForm;
5513
5985
  exports.RegisterTestIds = RegisterTestIds;
5514
5986
  exports.ResetPasswordError = ResetPasswordError;
5515
5987
  exports.ResetPasswordForm = ResetPasswordForm;
5988
+ exports.STEP_UP_PASSWORD_ENDPOINT = STEP_UP_PASSWORD_ENDPOINT;
5516
5989
  exports.SharedRegisterForm = SharedRegisterForm;
5990
+ exports.StepUpStatus = StepUpStatus;
5517
5991
  exports.VERIFY_AND_LOGIN_ENDPOINT = VERIFY_AND_LOGIN_ENDPOINT;
5518
5992
  exports.VERIFY_EMAIL_ENDPOINT = VERIFY_EMAIL_ENDPOINT;
5519
5993
  exports.VERIFY_TOKEN_PLACEHOLDER = VERIFY_TOKEN_PLACEHOLDER;
@@ -5524,6 +5998,7 @@ exports.buildVerifyUrlTemplate = buildVerifyUrlTemplate;
5524
5998
  exports.cachePreferredMethod = cachePreferredMethod;
5525
5999
  exports.clearCachedPreferredMethod = clearCachedPreferredMethod;
5526
6000
  exports.collectUserRoles = collectUserRoles;
6001
+ exports.createBffAccountDeletionClient = createBffAccountDeletionClient;
5527
6002
  exports.createBffAuthClient = createBffAuthClient;
5528
6003
  exports.createBffChangePasswordClient = createBffChangePasswordClient;
5529
6004
  exports.createBffVerifyEmailClient = createBffVerifyEmailClient;
@@ -5550,6 +6025,7 @@ exports.splitFullName = splitFullName;
5550
6025
  exports.startPasskeyLogin = startPasskeyLogin;
5551
6026
  exports.startPasskeyRegistration = startPasskeyRegistration;
5552
6027
  exports.syncPreferredMethodFromServer = syncPreferredMethodFromServer;
6028
+ exports.useAccountDeletion = useAccountDeletion;
5553
6029
  exports.useAuthTheme = useAuthTheme;
5554
6030
  exports.useBffAuth = useBffAuth;
5555
6031
  exports.useBffForgotPassword = useBffForgotPassword;