@rpcbase/auth 0.186.0 → 0.188.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.
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/request-password-reset/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAO,MAAM,cAAc,CAAA;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,OAAO,KAAK,oBAAoB,MAAM,SAAS,CAAA;AAG/C,eAAO,MAAM,+BAA+B,KAAK,CAAA;AAsBjD,eAAO,MAAM,oBAAoB,EAAE,UAAU,CAC3C,oBAAoB,CAAC,cAAc,EACnC,oBAAoB,CAAC,eAAe,EACpC,eAAe,CA4GhB,CAAA;yBAEe,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/request-password-reset/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAE,UAAU,EAAO,MAAM,cAAc,CAAA;AAKnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,OAAO,KAAK,oBAAoB,MAAM,SAAS,CAAA;AAG/C,eAAO,MAAM,+BAA+B,KAAK,CAAA;AAsBjD,eAAO,MAAM,oBAAoB,EAAE,UAAU,CAC3C,oBAAoB,CAAC,cAAc,EACnC,oBAAoB,CAAC,eAAe,EACpC,eAAe,CAiHhB,CAAA;yBAEe,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/sign-up/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAO,UAAU,EAAE,MAAM,cAAc,CAAA;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,OAAO,KAAK,MAAM,MAAM,SAAS,CAAA;AAGjC,eAAO,MAAM,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,eAAe,EAAE,eAAe,CAyF7F,CAAA;yBAEe,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../../src/api/sign-up/handler.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,GAAG,EAAO,UAAU,EAAE,MAAM,cAAc,CAAA;AAKnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,OAAO,KAAK,MAAM,MAAM,SAAS,CAAA;AAGjC,eAAO,MAAM,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,eAAe,EAAE,eAAe,CA8F7F,CAAA;yBAEe,KAAK,GAAG,CAAC,eAAe,CAAC;AAAzC,wBAEC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignUpForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAW,MAAM,OAAO,CAAA;AAYzC,eAAO,MAAM,UAAU,GAAI,sCAIxB;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,4CAwEA,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/SignUpForm/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAC,SAAS,EAAW,MAAM,OAAO,CAAA;AAYzC,eAAO,MAAM,UAAU,GAAI,sCAIxB;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,4CA0EA,CAAA"}
@@ -0,0 +1,4 @@
1
+ export declare const DISPOSABLE_EMAIL_ERROR_CODE = "disposable_email";
2
+ export declare const DISPOSABLE_EMAIL_CACHE_IDLE_MS: number;
3
+ export declare const isDisposableEmailAddress: (email: unknown) => boolean;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/email-validation/index.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,2BAA2B,qBAAqB,CAAA;AAC7D,eAAO,MAAM,8BAA8B,QAAgB,CAAA;AAyB3D,eAAO,MAAM,wBAAwB,GAAI,OAAO,OAAO,KAAG,OAkBzD,CAAA"}
@@ -0,0 +1,40 @@
1
+ import { createRequire } from "node:module";
2
+ //#region src/email-validation/index.ts
3
+ var DISPOSABLE_EMAIL_ERROR_CODE = "disposable_email";
4
+ var DISPOSABLE_EMAIL_CACHE_IDLE_MS = 300 * 1e3;
5
+ var require = createRequire(import.meta.url);
6
+ var mailCheckerModulePath = require.resolve("mailchecker");
7
+ var disposableEmailDomains;
8
+ var cacheEvictionTimer;
9
+ var scheduleCacheEviction = () => {
10
+ if (cacheEvictionTimer) clearTimeout(cacheEvictionTimer);
11
+ cacheEvictionTimer = setTimeout(() => {
12
+ disposableEmailDomains = void 0;
13
+ cacheEvictionTimer = void 0;
14
+ delete require.cache[mailCheckerModulePath];
15
+ }, DISPOSABLE_EMAIL_CACHE_IDLE_MS);
16
+ cacheEvictionTimer.unref();
17
+ };
18
+ var getDisposableEmailDomains = () => {
19
+ disposableEmailDomains ??= require(mailCheckerModulePath).blacklist();
20
+ scheduleCacheEviction();
21
+ return disposableEmailDomains;
22
+ };
23
+ var isDisposableEmailAddress = (email) => {
24
+ if (typeof email !== "string") return false;
25
+ const parts = email.trim().toLowerCase().split("@");
26
+ if (parts.length !== 2 || !parts[0] || !parts[1]) return false;
27
+ const disposableEmailDomains = getDisposableEmailDomains();
28
+ let domain = parts[1];
29
+ while (domain) {
30
+ if (disposableEmailDomains.has(domain)) return true;
31
+ const nextDot = domain.indexOf(".");
32
+ if (nextDot === -1) return false;
33
+ domain = domain.slice(nextDot + 1);
34
+ }
35
+ return false;
36
+ };
37
+ //#endregion
38
+ export { DISPOSABLE_EMAIL_CACHE_IDLE_MS, DISPOSABLE_EMAIL_ERROR_CODE, isDisposableEmailAddress };
39
+
40
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["createRequire","MailChecker","DISPOSABLE_EMAIL_ERROR_CODE","DISPOSABLE_EMAIL_CACHE_IDLE_MS","require","import","meta","url","mailCheckerModulePath","resolve","disposableEmailDomains","Set","cacheEvictionTimer","ReturnType","setTimeout","scheduleCacheEviction","clearTimeout","undefined","cache","unref","getDisposableEmailDomains","blacklist","isDisposableEmailAddress","email","parts","trim","toLowerCase","split","length","domain","has","nextDot","indexOf","slice"],"sources":["../../src/email-validation/index.ts"],"sourcesContent":["import { createRequire } from \"node:module\"\n\nimport type MailChecker from \"mailchecker\"\n\n\nexport const DISPOSABLE_EMAIL_ERROR_CODE = \"disposable_email\"\nexport const DISPOSABLE_EMAIL_CACHE_IDLE_MS = 5 * 60 * 1000\n\nconst require = createRequire(import.meta.url)\nconst mailCheckerModulePath = require.resolve(\"mailchecker\")\n\nlet disposableEmailDomains: Set<string> | undefined\nlet cacheEvictionTimer: ReturnType<typeof setTimeout> | undefined\n\nconst scheduleCacheEviction = (): void => {\n if (cacheEvictionTimer) clearTimeout(cacheEvictionTimer)\n\n cacheEvictionTimer = setTimeout(() => {\n disposableEmailDomains = undefined\n cacheEvictionTimer = undefined\n delete require.cache[mailCheckerModulePath]\n }, DISPOSABLE_EMAIL_CACHE_IDLE_MS)\n cacheEvictionTimer.unref()\n}\n\nconst getDisposableEmailDomains = (): Set<string> => {\n disposableEmailDomains ??= (require(mailCheckerModulePath) as typeof MailChecker).blacklist()\n scheduleCacheEviction()\n return disposableEmailDomains\n}\n\nexport const isDisposableEmailAddress = (email: unknown): boolean => {\n if (typeof email !== \"string\") return false\n\n const parts = email.trim().toLowerCase().split(\"@\")\n if (parts.length !== 2 || !parts[0] || !parts[1]) return false\n\n const disposableEmailDomains = getDisposableEmailDomains()\n let domain = parts[1]\n\n while (domain) {\n if (disposableEmailDomains.has(domain)) return true\n\n const nextDot = domain.indexOf(\".\")\n if (nextDot === -1) return false\n domain = domain.slice(nextDot + 1)\n }\n\n return false\n}\n"],"mappings":";;AAKA,IAAaE,8BAA8B;AAC3C,IAAaC,iCAAiC,MAAS;AAEvD,IAAMC,UAAUJ,cAAcK,OAAOC,KAAKC,GAAG;AAC7C,IAAMC,wBAAwBJ,QAAQK,QAAQ,aAAa;AAE3D,IAAIC;AACJ,IAAIE;AAEJ,IAAMG,8BAAoC;CACxC,IAAIH,oBAAoBI,aAAaJ,kBAAkB;CAEvDA,qBAAqBE,iBAAiB;EACpCJ,yBAAyBO,KAAAA;EACzBL,qBAAqBK,KAAAA;EACrB,OAAOb,QAAQc,MAAMV;CACvB,GAAGL,8BAA8B;CACjCS,mBAAmBO,MAAM;AAC3B;AAEA,IAAMC,kCAA+C;CACnDV,2BAA4BN,QAAQI,qBAAqB,CAAC,CAAwBa,UAAU;CAC5FN,sBAAsB;CACtB,OAAOL;AACT;AAEA,IAAaY,4BAA4BC,UAA4B;CACnE,IAAI,OAAOA,UAAU,UAAU,OAAO;CAEtC,MAAMC,QAAQD,MAAME,KAAK,CAAC,CAACC,YAAY,CAAC,CAACC,MAAM,GAAG;CAClD,IAAIH,MAAMI,WAAW,KAAK,CAACJ,MAAM,MAAM,CAACA,MAAM,IAAI,OAAO;CAEzD,MAAMd,yBAAyBU,0BAA0B;CACzD,IAAIS,SAASL,MAAM;CAEnB,OAAOK,QAAQ;EACb,IAAInB,uBAAuBoB,IAAID,MAAM,GAAG,OAAO;EAE/C,MAAME,UAAUF,OAAOG,QAAQ,GAAG;EAClC,IAAID,YAAY,IAAI,OAAO;EAC3BF,SAASA,OAAOI,MAAMF,UAAU,CAAC;CACnC;CAEA,OAAO;AACT"}
@@ -1,11 +1,10 @@
1
1
  import { n as requestSchema, t as ROUTE } from "./sign-up-Di48rzbG.js";
2
+ import { DISPOSABLE_EMAIL_ERROR_CODE, isDisposableEmailAddress } from "./email-validation/index.js";
2
3
  import crypto from "crypto";
3
4
  import { createProvisionedTenant, models } from "@rpcbase/db";
4
5
  import { hashPasswordForStorage, sendEmail } from "@rpcbase/server";
5
6
  //#region src/api/sign-up/handler.ts
6
7
  var signUp = async (payload, ctx) => {
7
- const User = await models.getGlobal("RBUser", ctx);
8
- const Tenant = await models.getGlobal("RBTenant", ctx);
9
8
  const parsed = requestSchema.safeParse(payload);
10
9
  if (!parsed.success) {
11
10
  ctx.res.status(400);
@@ -15,6 +14,15 @@ var signUp = async (payload, ctx) => {
15
14
  };
16
15
  }
17
16
  const { email, password, rememberMe: _rememberMe } = parsed.data;
17
+ if (await isDisposableEmailAddress(email)) {
18
+ ctx.res.status(400);
19
+ return {
20
+ success: false,
21
+ error: DISPOSABLE_EMAIL_ERROR_CODE
22
+ };
23
+ }
24
+ const User = await models.getGlobal("RBUser", ctx);
25
+ const Tenant = await models.getGlobal("RBTenant", ctx);
18
26
  if (await User.findOne({ email })) {
19
27
  console.log("user with email already exists", email);
20
28
  ctx.res.status(409);
@@ -91,4 +99,4 @@ var handler_default = (api) => {
91
99
  //#endregion
92
100
  export { handler_default as default, signUp };
93
101
 
94
- //# sourceMappingURL=handler-BerdJCZB.js.map
102
+ //# sourceMappingURL=handler-B1ZE5nYO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-B1ZE5nYO.js","names":["crypto","Api","Ctx","ApiHandler","createProvisionedTenant","models","hashPasswordForStorage","sendEmail","DISPOSABLE_EMAIL_ERROR_CODE","isDisposableEmailAddress","AuthSessionUser","SignUp","signUp","RequestPayload","ResponsePayload","payload","ctx","Promise","parsed","requestSchema","safeParse","success","res","status","error","email","password","rememberMe","_rememberMe","data","User","getGlobal","Tenant","existingUser","findOne","console","log","hashedPassword","tenantId","randomUUID","emailVerificationCode","randomInt","toString","padStart","emailVerificationExpiresAt","Date","now","user","tenants","tenantRoles","save","to","subject","html","text","err","deleteOne","_id","rollbackErr","name","userId","api","post","ROUTE"],"sources":["../src/api/sign-up/handler.ts"],"sourcesContent":["import crypto from \"crypto\"\n\nimport { Api, Ctx, ApiHandler } from \"@rpcbase/api\"\nimport { createProvisionedTenant, models } from \"@rpcbase/db\"\nimport { hashPasswordForStorage, sendEmail } from \"@rpcbase/server\"\n\nimport { DISPOSABLE_EMAIL_ERROR_CODE, isDisposableEmailAddress } from \"../../email-validation\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SignUp from \"./index\"\n\n\nexport const signUp: ApiHandler<SignUp.RequestPayload, SignUp.ResponsePayload, AuthSessionUser> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SignUp.ResponsePayload> => {\n const parsed = SignUp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { email, password, rememberMe: _rememberMe } = parsed.data\n\n if (await isDisposableEmailAddress(email)) {\n ctx.res.status(400)\n return { success: false, error: DISPOSABLE_EMAIL_ERROR_CODE }\n }\n\n const User = await models.getGlobal(\"RBUser\", ctx)\n const Tenant = await models.getGlobal(\"RBTenant\", ctx)\n\n const existingUser = await User.findOne({ email })\n\n if (existingUser) {\n console.log(\"user with email already exists\", email)\n // For security, we don't want to reveal if an email is registered.\n // But for this implementation, we'll make it simple.\n ctx.res.status(409)\n return { success: false, error: \"user_exists\" }\n }\n\n const hashedPassword = await hashPasswordForStorage(password)\n\n const tenantId = crypto.randomUUID()\n\n const emailVerificationCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const emailVerificationExpiresAt = new Date(Date.now() + 10 * 60 * 1000)\n\n const user = new User({\n email,\n password: hashedPassword,\n tenants: [tenantId],\n tenantRoles: {\n [tenantId]: [\"owner\"],\n },\n emailVerificationCode,\n emailVerificationExpiresAt,\n })\n await user.save()\n\n try {\n await sendEmail({\n to: email,\n subject: `Verify your email: ${emailVerificationCode}`,\n html: `\n <p>Welcome to rpcbase!</p>\n <p>Your verification code is <strong>${emailVerificationCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Welcome to rpcbase! Your verification code is ${emailVerificationCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.error(\"failed to send sign-up email\", err)\n\n try {\n await User.deleteOne({ _id: user._id })\n } catch (rollbackErr) {\n console.error(\"failed to roll back user after sign-up email failure\", rollbackErr)\n }\n\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n try {\n await createProvisionedTenant(Tenant, {\n tenantId,\n name: email,\n })\n } catch (err) {\n console.error(\"failed to initialize tenant for user\", err)\n try {\n await User.deleteOne({ _id: user._id })\n } catch (rollbackErr) {\n console.error(\"failed to roll back user after tenant initialization failure\", rollbackErr)\n }\n ctx.res.status(500)\n return { success: false, error: \"tenant_initialization_failed\" }\n }\n\n console.log(\"created new user\", user._id.toString())\n\n return { success: true, userId: user._id.toString(), tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(SignUp.ROUTE, signUp)\n}\n"],"mappings":";;;;;;AAYA,IAAaY,SAAqF,OAChGG,SACAC,QACoC;CACpC,MAAME,SAAAA,cAA8BE,UAAUL,OAAO;CAErD,IAAI,CAACG,OAAOG,SAAS;EACnBL,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAGA,MAAM,EAAEC,OAAOC,UAAUC,YAAYC,gBAAgBV,OAAOW;CAE5D,IAAI,MAAMpB,yBAAyBgB,KAAK,GAAG;EACzCT,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAOhB;EAA4B;CAC9D;CAEA,MAAMsB,OAAO,MAAMzB,OAAO0B,UAAU,UAAUf,GAAG;CACjD,MAAMgB,SAAS,MAAM3B,OAAO0B,UAAU,YAAYf,GAAG;CAIrD,IAAIiB,MAFuBH,KAAKI,QAAQ,EAAET,MAAM,CAAC,GAE/B;EAChBU,QAAQC,IAAI,kCAAkCX,KAAK;EAGnDT,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAc;CAChD;CAEA,MAAMa,iBAAiB,MAAM/B,uBAAuBoB,QAAQ;CAE5D,MAAMY,WAAWtC,OAAOuC,WAAW;CAEnC,MAAMC,wBAAwBxC,OAAOyC,UAAU,GAAG,GAAS,CAAC,CAACC,SAAS,CAAC,CAACC,SAAS,GAAG,GAAG;CACvF,MAAMC,6BAA6B,IAAIC,KAAKA,KAAKC,IAAI,IAAI,MAAU,GAAI;CAEvE,MAAMC,OAAO,IAAIjB,KAAK;EACpBL;EACAC,UAAUW;EACVW,SAAS,CAACV,QAAQ;EAClBW,aAAa,GACVX,WAAW,CAAC,OAAO,EACtB;EACAE;EACAI;CACF,CAAC;CACD,MAAMG,KAAKG,KAAK;CAEhB,IAAI;EACF,MAAM3C,UAAU;GACd4C,IAAI1B;GACJ2B,SAAS,sBAAsBZ;GAC/Ba,MAAM;;+CAEmCb,sBAAqB;;;GAG9Dc,MAAM,iDAAiDd,sBAAqB;EAC9E,CAAC;CACH,SAASe,KAAK;EACZpB,QAAQX,MAAM,gCAAgC+B,GAAG;EAEjD,IAAI;GACF,MAAMzB,KAAK0B,UAAU,EAAEC,KAAKV,KAAKU,IAAI,CAAC;EACxC,SAASC,aAAa;GACpBvB,QAAQX,MAAM,wDAAwDkC,WAAW;EACnF;EAEA1C,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAwB;CAC1D;CAEA,IAAI;EACF,MAAMpB,wBAAwB4B,QAAQ;GACpCM;GACAqB,MAAMlC;EACR,CAAC;CACH,SAAS8B,KAAK;EACZpB,QAAQX,MAAM,wCAAwC+B,GAAG;EACzD,IAAI;GACF,MAAMzB,KAAK0B,UAAU,EAAEC,KAAKV,KAAKU,IAAI,CAAC;EACxC,SAASC,aAAa;GACpBvB,QAAQX,MAAM,gEAAgEkC,WAAW;EAC3F;EACA1C,IAAIM,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAA+B;CACjE;CAEAW,QAAQC,IAAI,oBAAoBW,KAAKU,IAAIf,SAAS,CAAC;CAEnD,OAAO;EAAErB,SAAS;EAAMuC,QAAQb,KAAKU,IAAIf,SAAS;EAAGJ;CAAS;AAChE;AAEA,IAAA,mBAAgBuB,QAA8B;CAC5CA,IAAIC,KAAKnD,OAAcC,MAAM;AAC/B"}
@@ -1,4 +1,5 @@
1
1
  import { a as string, i as object, n as boolean, r as number } from "./schemas-BgOpQqUv.js";
2
+ import { DISPOSABLE_EMAIL_ERROR_CODE, isDisposableEmailAddress } from "./email-validation/index.js";
2
3
  import crypto from "crypto";
3
4
  import { models } from "@rpcbase/db";
4
5
  import { sendEmail } from "@rpcbase/server";
@@ -26,7 +27,6 @@ var maskEmailForLog = (email) => {
26
27
  return `${localPrefix}${localMask}@${"*".repeat(Math.max(4, domain.length - domainTailLength))}${domainTail}`;
27
28
  };
28
29
  var requestPasswordReset = async (payload, ctx) => {
29
- const User = await models.getGlobal("RBUser", ctx);
30
30
  const parsed = requestSchema.safeParse(payload);
31
31
  if (!parsed.success) {
32
32
  ctx.res.status(400);
@@ -36,6 +36,14 @@ var requestPasswordReset = async (payload, ctx) => {
36
36
  };
37
37
  }
38
38
  const { email } = parsed.data;
39
+ if (await isDisposableEmailAddress(email)) {
40
+ ctx.res.status(400);
41
+ return {
42
+ success: false,
43
+ error: DISPOSABLE_EMAIL_ERROR_CODE
44
+ };
45
+ }
46
+ const User = await models.getGlobal("RBUser", ctx);
39
47
  const requestedAt = /* @__PURE__ */ new Date();
40
48
  const cooldownEligibleExpiresAt = new Date(requestedAt.getTime() + (PASSWORD_RESET_CODE_TTL_SECONDS - 60) * 1e3);
41
49
  const passwordResetCode = crypto.randomInt(0, 1e6).toString().padStart(6, "0");
@@ -122,4 +130,4 @@ var handler_default = (api) => {
122
130
  //#endregion
123
131
  export { PASSWORD_RESET_COOLDOWN_SECONDS, handler_default as default, requestPasswordReset };
124
132
 
125
- //# sourceMappingURL=handler-DOaODKMb.js.map
133
+ //# sourceMappingURL=handler-aS6g_PVg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-aS6g_PVg.js","names":["z","ROUTE","requestSchema","object","email","string","RequestPayload","infer","responseSchema","success","boolean","error","optional","retryAfterSeconds","number","int","positive","ResponsePayload","crypto","Api","ApiHandler","Ctx","models","sendEmail","DISPOSABLE_EMAIL_ERROR_CODE","isDisposableEmailAddress","AuthSessionUser","RequestPasswordReset","PASSWORD_RESET_COOLDOWN_SECONDS","PASSWORD_RESET_CODE_TTL_SECONDS","maskEmailForLog","email","localRaw","domainRaw","split","local","domain","localPrefix","slice","Math","min","length","localMask","repeat","max","domainTailLength","domainTail","domainMask","requestPasswordReset","RequestPayload","ResponsePayload","payload","ctx","Promise","parsed","requestSchema","safeParse","success","res","status","error","data","User","getGlobal","requestedAt","Date","cooldownEligibleExpiresAt","getTime","passwordResetCode","randomInt","toString","padStart","passwordResetCodeExpiresAt","user","findOneAndUpdate","$or","$exists","$lte","$set","$unset","passwordResetToken","passwordResetTokenExpiresAt","returnDocument","setHeader","String","existingUser","findOne","_id","console","info","to","subject","html","text","err","retryAfterSeconds","releaseResult","updateOne","modifiedCount","removeHeader","releaseErr","api","post","ROUTE"],"sources":["../src/api/request-password-reset/index.ts","../src/api/request-password-reset/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/request-password-reset\"\n\nexport const requestSchema = z.object({\n email: z.string().email(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n retryAfterSeconds: z.number().int().positive().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { sendEmail } from \"@rpcbase/server\"\n\nimport { DISPOSABLE_EMAIL_ERROR_CODE, isDisposableEmailAddress } from \"../../email-validation\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as RequestPasswordReset from \"./index\"\n\n\nexport const PASSWORD_RESET_COOLDOWN_SECONDS = 60\nconst PASSWORD_RESET_CODE_TTL_SECONDS = 10 * 60\n\nconst maskEmailForLog = (email: string) => {\n const [localRaw, domainRaw = \"\"] = email.split(\"@\")\n const local = localRaw || \"\"\n const domain = domainRaw || \"\"\n const localPrefix = local.slice(0, Math.min(3, local.length))\n const localMask = \"*\".repeat(Math.max(5, local.length - localPrefix.length))\n\n if (!domain) {\n return `${localPrefix}${localMask}@****`\n }\n\n const domainTailLength = Math.min(7, domain.length)\n const domainTail = domain.slice(-domainTailLength)\n const domainMask = \"*\".repeat(Math.max(4, domain.length - domainTailLength))\n\n return `${localPrefix}${localMask}@${domainMask}${domainTail}`\n}\n\n\nexport const requestPasswordReset: ApiHandler<\n RequestPasswordReset.RequestPayload,\n RequestPasswordReset.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<RequestPasswordReset.ResponsePayload> => {\n const parsed = RequestPasswordReset.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email } = parsed.data\n\n if (await isDisposableEmailAddress(email)) {\n ctx.res.status(400)\n return { success: false, error: DISPOSABLE_EMAIL_ERROR_CODE }\n }\n\n const User = await models.getGlobal(\"RBUser\", ctx)\n const requestedAt = new Date()\n const cooldownEligibleExpiresAt = new Date(\n requestedAt.getTime() + (PASSWORD_RESET_CODE_TTL_SECONDS - PASSWORD_RESET_COOLDOWN_SECONDS) * 1000,\n )\n const passwordResetCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const passwordResetCodeExpiresAt = new Date(requestedAt.getTime() + PASSWORD_RESET_CODE_TTL_SECONDS * 1000)\n\n const user = await User.findOneAndUpdate(\n {\n email,\n $or: [\n { passwordResetCodeExpiresAt: { $exists: false } },\n { passwordResetCodeExpiresAt: { $lte: cooldownEligibleExpiresAt } },\n ],\n },\n {\n $set: {\n passwordResetCode,\n passwordResetCodeExpiresAt,\n },\n $unset: {\n passwordResetToken: \"\",\n passwordResetTokenExpiresAt: \"\",\n },\n },\n { returnDocument: \"after\" },\n )\n\n ctx.res.setHeader(\"Retry-After\", String(PASSWORD_RESET_COOLDOWN_SECONDS))\n\n if (!user) {\n const existingUser = await User.findOne({ email }, { _id: 1 })\n\n if (!existingUser) {\n try {\n console.info(`sending password reset account notice to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: \"No account found for your password reset request\",\n html: `\n <p>Hello,</p>\n <p>You requested a new password reset code for this email address, but unfortunately we couldn't find an account associated with it.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n <p>If you're having trouble accessing your account, please contact support.</p>\n `,\n text: \"Hello, You requested a new password reset code for this email address, but unfortunately we couldn't find an account associated with it. If you didn't request this, you can ignore this message. If you're having trouble accessing your account, please contact support.\",\n })\n } catch (err) {\n console.error(\"failed to send password reset account notice email\", err)\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n }\n\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n }\n\n try {\n console.info(`sending password reset to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: `Your password reset code: ${passwordResetCode}`,\n html: `\n <p>Your password reset code is <strong>${passwordResetCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Your password reset code is ${passwordResetCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.error(\"failed to send password reset email\", err)\n\n try {\n const releaseResult = await User.updateOne(\n { _id: user._id, passwordResetCode },\n {\n $unset: {\n passwordResetCode: \"\",\n passwordResetCodeExpiresAt: \"\",\n },\n },\n )\n if (releaseResult.modifiedCount > 0) {\n ctx.res.removeHeader(\"Retry-After\")\n }\n } catch (releaseErr) {\n console.error(\"failed to release password reset cooldown\", releaseErr)\n }\n\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(RequestPasswordReset.ROUTE, requestPasswordReset)\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -273,7 +273,7 @@ var SignUpForm = ({ children, className, otpNextPath }) => {
273
273
  });
274
274
  const json = await res.json();
275
275
  if (!res.ok) {
276
- const message = json.error === "user_exists" ? "An account already exists with this email." : "Sign-up failed. Please try again.";
276
+ const message = json.error === "user_exists" ? "An account already exists with this email." : json.error === "disposable_email" ? "Disposable email addresses are not allowed." : "Sign-up failed. Please try again.";
277
277
  methods.setError("root", {
278
278
  type: "server",
279
279
  message
@@ -739,10 +739,12 @@ var ResendCodeButton = ({ seconds = 60, onResend, className, disabled }) => {
739
739
  var DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS = 60;
740
740
  var PasswordResetCodeRequestError = class extends Error {
741
741
  retryAfterSeconds;
742
- constructor(retryAfterSeconds) {
742
+ errorCode;
743
+ constructor(retryAfterSeconds, errorCode) {
743
744
  super("Unable to request a password reset code");
744
745
  this.name = "PasswordResetCodeRequestError";
745
746
  this.retryAfterSeconds = retryAfterSeconds;
747
+ this.errorCode = errorCode;
746
748
  }
747
749
  };
748
750
  var parseRetryAfterSeconds = (value) => {
@@ -758,7 +760,7 @@ var requestPasswordResetCode = async (email) => {
758
760
  });
759
761
  const payload = await response.json().catch(() => null);
760
762
  const retryAfterSeconds = parseRetryAfterSeconds(payload?.retryAfterSeconds) ?? parseRetryAfterSeconds(response.headers.get("Retry-After"));
761
- if (!response.ok || payload?.success !== true) throw new PasswordResetCodeRequestError(retryAfterSeconds);
763
+ if (!response.ok || payload?.success !== true) throw new PasswordResetCodeRequestError(retryAfterSeconds, typeof payload?.error === "string" ? payload.error : void 0);
762
764
  return { retryAfterSeconds: retryAfterSeconds ?? 60 };
763
765
  };
764
766
  var usePasswordResetResend = ({ email, cooldownSeconds = 60 }) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["React","Link","useLocation","LINKS_REDIRECTION_MAP","Pathname","AuthLayout","t0","$","_c","sidePanel","t1","children","undefined","location","linkTitle","pathname","title","linkLocation","t2","t3","t4","t5","clsx","AppleSignInButton","t0","$","_c","onPress","className","t1","t2","t3","Symbol","for","t4","useFormContext","EmailInput","t0","$","_c","id","className","placeholder","register","formState","error","errors","email","errorMessage","message","t1","t2","t3","t4","hasUnsafeNextPathChars","value","i","length","code","charCodeAt","sanitizeNextPath","raw","nextPath","trim","startsWith","base","URL","url","origin","pathname","search","hash","ReactNode","useNavigate","useSearchParams","useForm","FormProvider","zodResolver","SubmitHandler","z","requestSchema","sanitizeNextPath","SignInFormValues","input","SignInForm","children","className","navigate","searchParams","methods","defaultValues","email","password","rememberMe","navigationGuard","resolver","onSubmit","data","clearErrors","res","fetch","method","headers","body","JSON","stringify","credentials","json","catch","success","error","ok","message","setError","type","nextPath","get","replace","err","handleSubmit","formState","errors","root","ReactNode","useState","useNavigate","useForm","FormProvider","zodResolver","SubmitHandler","z","requestSchema","SignUpFormValues","input","SignUpForm","children","className","otpNextPath","navigate","serverMessage","setServerMessage","methods","defaultValues","email","password","rememberMe","navigationGuard","resolver","onSubmit","data","clearErrors","res","fetch","method","headers","body","JSON","stringify","credentials","json","ok","message","error","setError","type","success","search","URLSearchParams","set","toString","err","handleSubmit","formState","errors","root","ElementType","useFormContext","RememberMeCheckbox","t0","$","_c","label","as","t1","Component","undefined","register","t2","t3","t4","t5","__iconNode","useState","Eye","EyeOff","useFormContext","PasswordInputProps","id","name","className","placeholder","autoComplete","PasswordInput","t0","$","_c","t1","t2","undefined","showPassword","setShowPassword","register","formState","fieldError","errors","errorMessage","message","inputClassName","t3","t4","Parameters","t5","t6","t7","Symbol","for","_temp","t8","t9","t10","t11","t12","t13","prev","useEffect","useState","clsx","useResendCountdown","t0","t1","$","_c","seconds","undefined","initiallyCountingDown","remaining","setRemaining","isCountingDown","setIsCountingDown","t2","t3","timer","setInterval","prev","clearInterval","t4","nextSeconds","value","restart","minutes","Math","floor","secs","t5","toString","padStart","formatted","canResend","t6","ResendCodeButton","onResend","className","disabled","Promise","isSending","setIsSending","handleClick","isDisabled","showCountdown","useRef","useState","useResendCountdown","DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS","PasswordResetCodeRequestResult","retryAfterSeconds","PasswordResetCodeRequestError","Error","constructor","name","parseRetryAfterSeconds","value","seconds","Number","isFinite","undefined","Math","ceil","requestPasswordResetCode","email","Promise","response","fetch","method","headers","body","JSON","stringify","payload","json","catch","success","get","ok","usePasswordResetResend","cooldownSeconds","countdown","isResending","setIsResending","isResendingRef","resend","canResend","current","result","restart","error","remainingSeconds","remaining","formatted","isCountingDown","Boolean"],"sources":["../src/components/AuthLayout/index.tsx","../src/components/AppleSignInButton/index.tsx","../src/components/EmailInput/index.tsx","../src/utils/sanitizeNextPath.ts","../src/components/SignInForm/index.tsx","../src/components/SignUpForm/index.tsx","../src/components/RememberMeCheckbox/index.tsx","../../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs","../../../node_modules/lucide-react/dist/esm/defaultAttributes.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs","../../../node_modules/lucide-react/dist/esm/context.mjs","../../../node_modules/lucide-react/dist/esm/Icon.mjs","../../../node_modules/lucide-react/dist/esm/createLucideIcon.mjs","../../../node_modules/lucide-react/dist/esm/icons/eye-off.mjs","../../../node_modules/lucide-react/dist/esm/icons/eye.mjs","../src/components/PasswordInput/index.tsx","../src/components/ResendCodeButton/index.tsx","../src/passwordReset.ts"],"sourcesContent":["import * as React from \"react\"\nimport {Link, useLocation } from \"@rpcbase/router\"\n\n\nconst LINKS_REDIRECTION_MAP = {\n \"/auth/sign-in\": {\n \"title\": \"Sign Up\",\n \"location\": \"/auth/sign-up\"\n },\n \"/auth/sign-up\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n },\n \"/auth/logout-success\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n }\n}\n\ntype Pathname = keyof typeof LINKS_REDIRECTION_MAP\n\nexport const AuthLayout = ({\n sidePanel = null,\n children,\n}: {\n sidePanel: React.ReactElement | null\n children: React.ReactNode\n}) => {\n const location = useLocation()\n\n const linkTitle = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.title\n const linkLocation = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.location\n\n return (\n <div className=\"container relative flex min-h-dvh flex-col items-center justify-center px-4 py-20 sm:px-6 md:px-8 lg:grid lg:h-dvh lg:w-full lg:max-w-none lg:grid-cols-2 lg:px-0 lg:py-0\">\n {/* Top-right redirection link */}\n {linkTitle && linkLocation ? (\n <Link\n className=\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2 absolute right-4 top-4 md:right-8 md:top-8\"\n to={linkLocation}\n >\n {linkTitle}\n </Link>\n ) : null}\n <div className=\"relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex\">\n {sidePanel}\n </div>\n <div className=\"mx-auto flex w-full flex-col justify-center gap-6 /*sm:w-[350px]*/\">\n {children}\n </div>\n </div>\n )\n}\n","import clsx from \"clsx\"\n\n\nexport const AppleSignInButton = ({\n onPress,\n className,\n}: {\n onPress: () => void;\n className?: string;\n}) => {\n\n return (\n <button\n onClick={onPress}\n className={clsx(`\n w-full\n bg-black text-white hover:bg-gray-800\n flex items-center justify-center\n px-6 py-2\n rounded-lg\n font-medium\n transition duration-150 ease-in-out\n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black\n `, className)}\n >\n <svg\n className=\"w-5 h-5 mr-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"white\"\n >\n <path d=\"M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.539 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701\" />\n </svg>\n <span>Continue with Apple</span>\n </button>\n )\n}\n","import {useFormContext} from \"@rpcbase/form\"\n\n\nexport const EmailInput = ({\n id,\n className,\n placeholder,\n}: {\n id: string\n className?: string\n placeholder?: string\n}) => {\n const {register, formState} = useFormContext()\n\n const error = formState.errors.email\n let errorMessage: string | undefined\n if (typeof error === \"string\") {\n errorMessage = error\n } else if (error && typeof error.message === \"string\") {\n errorMessage = error.message\n }\n\n return (\n <>\n <input\n id={id}\n type=\"email\"\n required\n autoComplete=\"email\"\n className={className}\n placeholder={placeholder}\n {...register(\"email\")}\n />\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","const hasUnsafeNextPathChars = (value: string) => {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i)\n if (code === 92 || code <= 31 || code === 127) return true\n }\n return false\n}\n\nexport const sanitizeNextPath = (raw: string | null) => {\n if (!raw) return null\n\n const nextPath = raw.trim()\n if (!nextPath) return null\n\n if (!nextPath.startsWith(\"/\") || nextPath.startsWith(\"//\")) {\n return null\n }\n\n if (hasUnsafeNextPathChars(nextPath)) {\n return null\n }\n\n try {\n const base = new URL(\"http://localhost\")\n const url = new URL(nextPath, base)\n if (url.origin !== base.origin) {\n return null\n }\n return `${url.pathname}${url.search}${url.hash}`\n } catch {\n return null\n }\n}\n\n","import { ReactNode } from \"react\"\nimport { useNavigate, useSearchParams } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type { SubmitHandler } from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-in\"\nimport { sanitizeNextPath } from \"../../utils/sanitizeNextPath\"\n\n\ntype SignInFormValues = z.input<typeof requestSchema>\n\nexport const SignInForm = ({\n children,\n className\n}: {\n children: ReactNode,\n className?: string\n}) => {\n const navigate = useNavigate()\n const [searchParams] = useSearchParams()\n\n const methods = useForm<SignInFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignInFormValues> = async (data) => {\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-in\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json().catch(() => null) as { success?: boolean; error?: string } | null\n\n if (!res.ok || !json?.success) {\n const message = json?.error === \"invalid_credentials\"\n ? \"Invalid email or password.\"\n : \"Sign-in failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n const nextPath = sanitizeNextPath(searchParams.get(\"next\")) ?? \"/\"\n navigate(nextPath, { replace: true })\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import {ReactNode, useState} from \"react\"\nimport { useNavigate } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type {SubmitHandler} from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-up\"\n\n\ntype SignUpFormValues = z.input<typeof requestSchema>\n\n\nexport const SignUpForm = ({\n children,\n className,\n otpNextPath\n}: {\n children: ReactNode,\n className?: string\n otpNextPath?: string\n}) => {\n\n const navigate = useNavigate()\n\n const [serverMessage, setServerMessage] = useState<string | null>(null)\n const methods = useForm<SignUpFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignUpFormValues> = async (data) => {\n setServerMessage(null)\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-up\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json()\n\n if (!res.ok) {\n const message = json.error === \"user_exists\"\n ? \"An account already exists with this email.\"\n : \"Sign-up failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n if (!json.success) {\n methods.setError(\"root\", { type: \"server\", message: \"Sign-up failed. Please try again.\" })\n return\n }\n\n const search = new URLSearchParams({ email: data.email })\n if (otpNextPath) {\n search.set(\"next\", otpNextPath)\n }\n navigate(`/auth/sign-up-otp?${search.toString()}`)\n setServerMessage(\"Account created. Check your inbox to verify your email.\")\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n {serverMessage && (\n <p className=\"mt-2 text-sm text-green-700\" role=\"status\">\n {serverMessage}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import type { ElementType } from \"react\"\nimport {useFormContext} from \"@rpcbase/form\"\n\n\nexport const RememberMeCheckbox = ({\n label,\n as: Component = \"input\"\n}: {\n label: string,\n as?: ElementType\n}) => {\n const {register} = useFormContext()\n\n return (\n <>\n <Component\n id=\"rememberMe\"\n {...register(\"rememberMe\")}\n />\n <label\n htmlFor={\"rememberMe\"}\n className=\"pl-2 block text-sm/6 text-gray-900\"\n >\n {label}\n </label>\n </>\n )\n}\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\n\nexport { mergeClasses };\n//# sourceMappingURL=mergeClasses.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n\nexport { toKebabCase };\n//# sourceMappingURL=toKebabCase.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\n\nexport { toCamelCase };\n//# sourceMappingURL=toCamelCase.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { toCamelCase } from './toCamelCase.mjs';\n\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\n\nexport { toPascalCase };\n//# sourceMappingURL=toPascalCase.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst hasA11yProp = (props) => {\n for (const prop in props) {\n if (prop.startsWith(\"aria-\") || prop === \"role\" || prop === \"title\") {\n return true;\n }\n }\n return false;\n};\n\nexport { hasA11yProp };\n//# sourceMappingURL=hasA11yProp.mjs.map\n","\"use strict\";\n\"use client\";\n/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { createContext, useContext, useMemo, createElement } from 'react';\n\nconst LucideContext = createContext({});\nfunction LucideProvider({\n children,\n size,\n color,\n strokeWidth,\n absoluteStrokeWidth,\n className\n}) {\n const value = useMemo(\n () => ({\n size,\n color,\n strokeWidth,\n absoluteStrokeWidth,\n className\n }),\n [size, color, strokeWidth, absoluteStrokeWidth, className]\n );\n return createElement(LucideContext.Provider, { value }, children);\n}\nconst useLucideContext = () => useContext(LucideContext);\n\nexport { LucideProvider, useLucideContext };\n//# sourceMappingURL=context.mjs.map\n","\"use strict\";\n\"use client\";\n/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes.mjs';\nimport { hasA11yProp } from './shared/src/utils/hasA11yProp.mjs';\nimport { mergeClasses } from './shared/src/utils/mergeClasses.mjs';\nimport { useLucideContext } from './context.mjs';\n\nconst Icon = forwardRef(\n ({ color, size, strokeWidth, absoluteStrokeWidth, className = \"\", children, iconNode, ...rest }, ref) => {\n const {\n size: contextSize = 24,\n strokeWidth: contextStrokeWidth = 2,\n absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,\n color: contextColor = \"currentColor\",\n className: contextClass = \"\"\n } = useLucideContext() ?? {};\n const calculatedStrokeWidth = absoluteStrokeWidth ?? contextAbsoluteStrokeWidth ? Number(strokeWidth ?? contextStrokeWidth) * 24 / Number(size ?? contextSize) : strokeWidth ?? contextStrokeWidth;\n return createElement(\n \"svg\",\n {\n ref,\n ...defaultAttributes,\n width: size ?? contextSize ?? defaultAttributes.width,\n height: size ?? contextSize ?? defaultAttributes.height,\n stroke: color ?? contextColor,\n strokeWidth: calculatedStrokeWidth,\n className: mergeClasses(\"lucide\", contextClass, className),\n ...!children && !hasA11yProp(rest) && { \"aria-hidden\": \"true\" },\n ...rest\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...Array.isArray(children) ? children : [children]\n ]\n );\n }\n);\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport { mergeClasses } from './shared/src/utils/mergeClasses.mjs';\nimport { toKebabCase } from './shared/src/utils/toKebabCase.mjs';\nimport { toPascalCase } from './shared/src/utils/toPascalCase.mjs';\nimport Icon from './Icon.mjs';\n\nconst createLucideIcon = (iconName, iconNode) => {\n const Component = forwardRef(\n ({ className, ...props }, ref) => createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className\n ),\n ...props\n })\n );\n Component.displayName = toPascalCase(iconName);\n return Component;\n};\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",\n key: \"ct8e1f\"\n }\n ],\n [\"path\", { d: \"M14.084 14.158a3 3 0 0 1-4.242-4.242\", key: \"151rxh\" }],\n [\n \"path\",\n {\n d: \"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",\n key: \"13bj9a\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n];\nconst EyeOff = createLucideIcon(\"eye-off\", __iconNode);\n\nexport { __iconNode, EyeOff as default };\n//# sourceMappingURL=eye-off.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Eye = createLucideIcon(\"eye\", __iconNode);\n\nexport { __iconNode, Eye as default };\n//# sourceMappingURL=eye.mjs.map\n","import { useState } from \"react\"\nimport { Eye, EyeOff } from \"lucide-react\"\nimport { useFormContext } from \"@rpcbase/form\"\n\n\ntype PasswordInputProps = {\n id: string\n name?: string\n className?: string\n placeholder?: string\n autoComplete?: string\n}\n\nexport const PasswordInput = ({\n id,\n name = \"password\",\n className,\n placeholder,\n autoComplete = \"current-password\",\n}: PasswordInputProps) => {\n const [showPassword, setShowPassword] = useState(false)\n const { register, formState } = useFormContext()\n\n const fieldError = formState.errors[name as keyof typeof formState.errors]\n const errorMessage =\n typeof fieldError === \"string\"\n ? fieldError\n : typeof fieldError?.message === \"string\"\n ? fieldError.message\n : undefined\n\n const inputClassName = className ? `${className} pr-11` : \"pr-11\"\n\n return (\n <>\n <div className=\"relative\">\n <input\n id={id}\n type={showPassword ? \"text\" : \"password\"}\n autoComplete={autoComplete}\n className={inputClassName}\n placeholder={placeholder}\n {...register(name as Parameters<typeof register>[0])}\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500\"\n onClick={() => setShowPassword((prev) => !prev)}\n aria-label={showPassword ? \"Hide characters\" : \"Show characters\"}\n >\n {showPassword ? (\n <EyeOff className=\"h-5 w-5\" aria-hidden />\n ) : (\n <Eye className=\"h-5 w-5\" aria-hidden />\n )}\n </button>\n </div>\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","import {useEffect, useState} from \"react\"\nimport clsx from \"clsx\"\n\n\nexport const useResendCountdown = (seconds = 60, initiallyCountingDown = true) => {\n const [remaining, setRemaining] = useState(seconds)\n const [isCountingDown, setIsCountingDown] = useState(initiallyCountingDown)\n\n useEffect(() => {\n if (!isCountingDown) return\n\n const timer = setInterval(() => {\n setRemaining((prev) => {\n if (prev <= 1) {\n setIsCountingDown(false)\n return seconds\n }\n return prev - 1\n })\n }, 1000)\n\n return () => clearInterval(timer)\n }, [isCountingDown, seconds])\n\n const restart = (nextSeconds?: number) => {\n const value = typeof nextSeconds === \"number\" ? nextSeconds : seconds\n setRemaining(value)\n setIsCountingDown(true)\n }\n\n const minutes = Math.floor(remaining / 60)\n const secs = remaining % 60\n const formatted = `${minutes}:${secs.toString().padStart(2, \"0\")}`\n\n const canResend = !isCountingDown\n\n return {remaining, formatted, isCountingDown, canResend, restart}\n}\n\n\nexport const ResendCodeButton = ({\n seconds = 60,\n onResend,\n className,\n disabled,\n}: {\n seconds?: number\n onResend?: () => Promise<void> | void\n className?: string\n disabled?: boolean\n}) => {\n const [isSending, setIsSending] = useState(false)\n const {formatted, isCountingDown, restart, canResend} = useResendCountdown(seconds)\n\n const handleClick = async () => {\n if (!canResend || isSending || disabled) return\n\n try {\n setIsSending(true)\n if (onResend) {\n await onResend()\n }\n } finally {\n restart()\n setIsSending(false)\n }\n }\n\n const isDisabled = disabled || isCountingDown || isSending\n const showCountdown = isDisabled && (isCountingDown || isSending)\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={isDisabled}\n aria-label=\"Resend code\"\n className={clsx(\n \"text-sm font-semibold text-sky-600 hover:underline disabled:cursor-not-allowed disabled:text-gray-400\",\n className\n )}\n >\n Resend code\n </button>\n {showCountdown ? (\n <span\n data-testid=\"resend-countdown\"\n className=\"text-sm text-gray-500\"\n aria-live=\"polite\"\n >\n ({formatted})\n </span>\n ) : null}\n </span>\n )\n}\n","import { useRef, useState } from \"react\"\n\nimport { useResendCountdown } from \"./components/ResendCodeButton\"\n\n\nexport const DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS = 60\n\nexport type PasswordResetCodeRequestResult = {\n retryAfterSeconds: number\n}\n\nexport class PasswordResetCodeRequestError extends Error {\n retryAfterSeconds?: number\n\n constructor(retryAfterSeconds?: number) {\n super(\"Unable to request a password reset code\")\n this.name = \"PasswordResetCodeRequestError\"\n this.retryAfterSeconds = retryAfterSeconds\n }\n}\n\nconst parseRetryAfterSeconds = (value: unknown) => {\n const seconds = typeof value === \"number\" ? value : Number(value)\n if (!Number.isFinite(seconds) || seconds <= 0) return undefined\n return Math.ceil(seconds)\n}\n\nexport const requestPasswordResetCode = async(email: string): Promise<PasswordResetCodeRequestResult> => {\n const response = await fetch(\"/api/rb/auth/request-password-reset\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email }),\n })\n\n const payload = await response.json().catch(() => null) as {\n success?: unknown\n retryAfterSeconds?: unknown\n } | null\n const retryAfterSeconds = parseRetryAfterSeconds(payload?.retryAfterSeconds)\n ?? parseRetryAfterSeconds(response.headers.get(\"Retry-After\"))\n\n if (!response.ok || payload?.success !== true) {\n throw new PasswordResetCodeRequestError(retryAfterSeconds)\n }\n\n return {\n retryAfterSeconds: retryAfterSeconds ?? DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS,\n }\n}\n\nexport const usePasswordResetResend = ({\n email,\n cooldownSeconds = DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS,\n}: {\n email?: string | null\n cooldownSeconds?: number\n}) => {\n const countdown = useResendCountdown(cooldownSeconds)\n const [isResending, setIsResending] = useState(false)\n const isResendingRef = useRef(false)\n\n const resend = async() => {\n if (!email || !countdown.canResend || isResendingRef.current) return null\n\n isResendingRef.current = true\n setIsResending(true)\n\n try {\n const result = await requestPasswordResetCode(email)\n countdown.restart(result.retryAfterSeconds)\n return result\n } catch (error) {\n if (error instanceof PasswordResetCodeRequestError && error.retryAfterSeconds) {\n countdown.restart(error.retryAfterSeconds)\n }\n throw error\n } finally {\n isResendingRef.current = false\n setIsResending(false)\n }\n }\n\n return {\n resend,\n isResending,\n remainingSeconds: countdown.remaining,\n formatted: countdown.formatted,\n isCountingDown: countdown.isCountingDown,\n canResend: Boolean(email) && countdown.canResend && !isResending,\n }\n}\n"],"x_google_ignoreList":[7,8,9,10,11,12,13,14,15,16,17],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["React","Link","useLocation","LINKS_REDIRECTION_MAP","Pathname","AuthLayout","t0","$","_c","sidePanel","t1","children","undefined","location","linkTitle","pathname","title","linkLocation","t2","t3","t4","t5","clsx","AppleSignInButton","t0","$","_c","onPress","className","t1","t2","t3","Symbol","for","t4","useFormContext","EmailInput","t0","$","_c","id","className","placeholder","register","formState","error","errors","email","errorMessage","message","t1","t2","t3","t4","hasUnsafeNextPathChars","value","i","length","code","charCodeAt","sanitizeNextPath","raw","nextPath","trim","startsWith","base","URL","url","origin","pathname","search","hash","ReactNode","useNavigate","useSearchParams","useForm","FormProvider","zodResolver","SubmitHandler","z","requestSchema","sanitizeNextPath","SignInFormValues","input","SignInForm","children","className","navigate","searchParams","methods","defaultValues","email","password","rememberMe","navigationGuard","resolver","onSubmit","data","clearErrors","res","fetch","method","headers","body","JSON","stringify","credentials","json","catch","success","error","ok","message","setError","type","nextPath","get","replace","err","handleSubmit","formState","errors","root","ReactNode","useState","useNavigate","useForm","FormProvider","zodResolver","SubmitHandler","z","requestSchema","SignUpFormValues","input","SignUpForm","children","className","otpNextPath","navigate","serverMessage","setServerMessage","methods","defaultValues","email","password","rememberMe","navigationGuard","resolver","onSubmit","data","clearErrors","res","fetch","method","headers","body","JSON","stringify","credentials","json","ok","message","error","setError","type","success","search","URLSearchParams","set","toString","err","handleSubmit","formState","errors","root","ElementType","useFormContext","RememberMeCheckbox","t0","$","_c","label","as","t1","Component","undefined","register","t2","t3","t4","t5","__iconNode","useState","Eye","EyeOff","useFormContext","PasswordInputProps","id","name","className","placeholder","autoComplete","PasswordInput","t0","$","_c","t1","t2","undefined","showPassword","setShowPassword","register","formState","fieldError","errors","errorMessage","message","inputClassName","t3","t4","Parameters","t5","t6","t7","Symbol","for","_temp","t8","t9","t10","t11","t12","t13","prev","useEffect","useState","clsx","useResendCountdown","t0","t1","$","_c","seconds","undefined","initiallyCountingDown","remaining","setRemaining","isCountingDown","setIsCountingDown","t2","t3","timer","setInterval","prev","clearInterval","t4","nextSeconds","value","restart","minutes","Math","floor","secs","t5","toString","padStart","formatted","canResend","t6","ResendCodeButton","onResend","className","disabled","Promise","isSending","setIsSending","handleClick","isDisabled","showCountdown","useRef","useState","useResendCountdown","DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS","PasswordResetCodeRequestResult","retryAfterSeconds","PasswordResetCodeRequestError","Error","errorCode","constructor","name","parseRetryAfterSeconds","value","seconds","Number","isFinite","undefined","Math","ceil","requestPasswordResetCode","email","Promise","response","fetch","method","headers","body","JSON","stringify","payload","json","catch","success","error","get","ok","usePasswordResetResend","cooldownSeconds","countdown","isResending","setIsResending","isResendingRef","resend","canResend","current","result","restart","remainingSeconds","remaining","formatted","isCountingDown","Boolean"],"sources":["../src/components/AuthLayout/index.tsx","../src/components/AppleSignInButton/index.tsx","../src/components/EmailInput/index.tsx","../src/utils/sanitizeNextPath.ts","../src/components/SignInForm/index.tsx","../src/components/SignUpForm/index.tsx","../src/components/RememberMeCheckbox/index.tsx","../../../node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs","../../../node_modules/lucide-react/dist/esm/defaultAttributes.mjs","../../../node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs","../../../node_modules/lucide-react/dist/esm/context.mjs","../../../node_modules/lucide-react/dist/esm/Icon.mjs","../../../node_modules/lucide-react/dist/esm/createLucideIcon.mjs","../../../node_modules/lucide-react/dist/esm/icons/eye-off.mjs","../../../node_modules/lucide-react/dist/esm/icons/eye.mjs","../src/components/PasswordInput/index.tsx","../src/components/ResendCodeButton/index.tsx","../src/passwordReset.ts"],"sourcesContent":["import * as React from \"react\"\nimport {Link, useLocation } from \"@rpcbase/router\"\n\n\nconst LINKS_REDIRECTION_MAP = {\n \"/auth/sign-in\": {\n \"title\": \"Sign Up\",\n \"location\": \"/auth/sign-up\"\n },\n \"/auth/sign-up\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n },\n \"/auth/logout-success\": {\n \"title\": \"Sign In\",\n \"location\": \"/auth/sign-in\"\n }\n}\n\ntype Pathname = keyof typeof LINKS_REDIRECTION_MAP\n\nexport const AuthLayout = ({\n sidePanel = null,\n children,\n}: {\n sidePanel: React.ReactElement | null\n children: React.ReactNode\n}) => {\n const location = useLocation()\n\n const linkTitle = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.title\n const linkLocation = LINKS_REDIRECTION_MAP[location.pathname as Pathname]?.location\n\n return (\n <div className=\"container relative flex min-h-dvh flex-col items-center justify-center px-4 py-20 sm:px-6 md:px-8 lg:grid lg:h-dvh lg:w-full lg:max-w-none lg:grid-cols-2 lg:px-0 lg:py-0\">\n {/* Top-right redirection link */}\n {linkTitle && linkLocation ? (\n <Link\n className=\"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2 absolute right-4 top-4 md:right-8 md:top-8\"\n to={linkLocation}\n >\n {linkTitle}\n </Link>\n ) : null}\n <div className=\"relative hidden h-full flex-col bg-muted p-10 text-white dark:border-r lg:flex\">\n {sidePanel}\n </div>\n <div className=\"mx-auto flex w-full flex-col justify-center gap-6 /*sm:w-[350px]*/\">\n {children}\n </div>\n </div>\n )\n}\n","import clsx from \"clsx\"\n\n\nexport const AppleSignInButton = ({\n onPress,\n className,\n}: {\n onPress: () => void;\n className?: string;\n}) => {\n\n return (\n <button\n onClick={onPress}\n className={clsx(`\n w-full\n bg-black text-white hover:bg-gray-800\n flex items-center justify-center\n px-6 py-2\n rounded-lg\n font-medium\n transition duration-150 ease-in-out\n focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-black\n `, className)}\n >\n <svg\n className=\"w-5 h-5 mr-3\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"white\"\n >\n <path d=\"M12.152 6.896c-.948 0-2.415-1.078-3.96-1.04-2.04.027-3.91 1.183-4.961 3.014-2.117 3.675-.539 9.103 1.519 12.09 1.013 1.454 2.208 3.09 3.792 3.039 1.52-.065 2.09-.987 3.935-.987 1.831 0 2.35.987 3.96.948 1.637-.026 2.676-1.48 3.676-2.948 1.156-1.688 1.636-3.325 1.662-3.415-.039-.013-3.182-1.221-3.22-4.857-.026-3.04 2.48-4.494 2.597-4.559-1.429-2.09-3.623-2.324-4.39-2.376-2-.156-3.675 1.09-4.61 1.09zM15.53 3.83c.843-1.012 1.4-2.427 1.245-3.83-1.207.052-2.662.805-3.532 1.818-.78.896-1.454 2.338-1.273 3.714 1.338.104 2.715-.688 3.559-1.701\" />\n </svg>\n <span>Continue with Apple</span>\n </button>\n )\n}\n","import {useFormContext} from \"@rpcbase/form\"\n\n\nexport const EmailInput = ({\n id,\n className,\n placeholder,\n}: {\n id: string\n className?: string\n placeholder?: string\n}) => {\n const {register, formState} = useFormContext()\n\n const error = formState.errors.email\n let errorMessage: string | undefined\n if (typeof error === \"string\") {\n errorMessage = error\n } else if (error && typeof error.message === \"string\") {\n errorMessage = error.message\n }\n\n return (\n <>\n <input\n id={id}\n type=\"email\"\n required\n autoComplete=\"email\"\n className={className}\n placeholder={placeholder}\n {...register(\"email\")}\n />\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","const hasUnsafeNextPathChars = (value: string) => {\n for (let i = 0; i < value.length; i++) {\n const code = value.charCodeAt(i)\n if (code === 92 || code <= 31 || code === 127) return true\n }\n return false\n}\n\nexport const sanitizeNextPath = (raw: string | null) => {\n if (!raw) return null\n\n const nextPath = raw.trim()\n if (!nextPath) return null\n\n if (!nextPath.startsWith(\"/\") || nextPath.startsWith(\"//\")) {\n return null\n }\n\n if (hasUnsafeNextPathChars(nextPath)) {\n return null\n }\n\n try {\n const base = new URL(\"http://localhost\")\n const url = new URL(nextPath, base)\n if (url.origin !== base.origin) {\n return null\n }\n return `${url.pathname}${url.search}${url.hash}`\n } catch {\n return null\n }\n}\n\n","import { ReactNode } from \"react\"\nimport { useNavigate, useSearchParams } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type { SubmitHandler } from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-in\"\nimport { sanitizeNextPath } from \"../../utils/sanitizeNextPath\"\n\n\ntype SignInFormValues = z.input<typeof requestSchema>\n\nexport const SignInForm = ({\n children,\n className\n}: {\n children: ReactNode,\n className?: string\n}) => {\n const navigate = useNavigate()\n const [searchParams] = useSearchParams()\n\n const methods = useForm<SignInFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignInFormValues> = async (data) => {\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-in\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json().catch(() => null) as { success?: boolean; error?: string } | null\n\n if (!res.ok || !json?.success) {\n const message = json?.error === \"invalid_credentials\"\n ? \"Invalid email or password.\"\n : \"Sign-in failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n const nextPath = sanitizeNextPath(searchParams.get(\"next\")) ?? \"/\"\n navigate(nextPath, { replace: true })\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import {ReactNode, useState} from \"react\"\nimport { useNavigate } from \"@rpcbase/router\"\nimport { useForm, FormProvider, zodResolver } from \"@rpcbase/form\"\nimport type {SubmitHandler} from \"@rpcbase/form\"\nimport { z } from \"zod\"\n\nimport {requestSchema} from \"../../api/sign-up\"\n\n\ntype SignUpFormValues = z.input<typeof requestSchema>\n\n\nexport const SignUpForm = ({\n children,\n className,\n otpNextPath\n}: {\n children: ReactNode,\n className?: string\n otpNextPath?: string\n}) => {\n\n const navigate = useNavigate()\n\n const [serverMessage, setServerMessage] = useState<string | null>(null)\n const methods = useForm<SignUpFormValues>({\n defaultValues: {\n email: \"\",\n password: \"\",\n rememberMe: true,\n },\n navigationGuard: false,\n resolver: zodResolver(requestSchema)\n })\n\n const onSubmit: SubmitHandler<SignUpFormValues> = async (data) => {\n setServerMessage(null)\n methods.clearErrors(\"root\")\n\n try {\n const res = await fetch(\"/api/rb/auth/sign-up\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(data),\n credentials: \"include\",\n })\n\n const json = await res.json()\n\n if (!res.ok) {\n const message = json.error === \"user_exists\"\n ? \"An account already exists with this email.\"\n : json.error === \"disposable_email\"\n ? \"Disposable email addresses are not allowed.\"\n : \"Sign-up failed. Please try again.\"\n methods.setError(\"root\", { type: \"server\", message })\n return\n }\n\n if (!json.success) {\n methods.setError(\"root\", { type: \"server\", message: \"Sign-up failed. Please try again.\" })\n return\n }\n\n const search = new URLSearchParams({ email: data.email })\n if (otpNextPath) {\n search.set(\"next\", otpNextPath)\n }\n navigate(`/auth/sign-up-otp?${search.toString()}`)\n setServerMessage(\"Account created. Check your inbox to verify your email.\")\n } catch (err) {\n methods.setError(\"root\", { type: \"server\", message: \"Network error. Please try again.\" })\n }\n }\n\n return (\n <FormProvider {...methods}>\n <form method=\"post\" noValidate className={className} onSubmit={methods.handleSubmit(onSubmit)}>\n {children}\n {methods.formState.errors.root?.message && (\n <p className=\"mt-2 text-sm text-red-600\" role=\"alert\">\n {methods.formState.errors.root.message}\n </p>\n )}\n {serverMessage && (\n <p className=\"mt-2 text-sm text-green-700\" role=\"status\">\n {serverMessage}\n </p>\n )}\n </form>\n </FormProvider>\n )\n}\n","import type { ElementType } from \"react\"\nimport {useFormContext} from \"@rpcbase/form\"\n\n\nexport const RememberMeCheckbox = ({\n label,\n as: Component = \"input\"\n}: {\n label: string,\n as?: ElementType\n}) => {\n const {register} = useFormContext()\n\n return (\n <>\n <Component\n id=\"rememberMe\"\n {...register(\"rememberMe\")}\n />\n <label\n htmlFor={\"rememberMe\"}\n className=\"pl-2 block text-sm/6 text-gray-900\"\n >\n {label}\n </label>\n </>\n )\n}\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst mergeClasses = (...classes) => classes.filter((className, index, array) => {\n return Boolean(className) && className.trim() !== \"\" && array.indexOf(className) === index;\n}).join(\" \").trim();\n\nexport { mergeClasses };\n//# sourceMappingURL=mergeClasses.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n\nexport { toKebabCase };\n//# sourceMappingURL=toKebabCase.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst toCamelCase = (string) => string.replace(\n /^([A-Z])|[\\s-_]+(\\w)/g,\n (match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()\n);\n\nexport { toCamelCase };\n//# sourceMappingURL=toCamelCase.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { toCamelCase } from './toCamelCase.mjs';\n\nconst toPascalCase = (string) => {\n const camelCase = toCamelCase(string);\n return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);\n};\n\nexport { toPascalCase };\n//# sourceMappingURL=toPascalCase.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nvar defaultAttributes = {\n xmlns: \"http://www.w3.org/2000/svg\",\n width: 24,\n height: 24,\n viewBox: \"0 0 24 24\",\n fill: \"none\",\n stroke: \"currentColor\",\n strokeWidth: 2,\n strokeLinecap: \"round\",\n strokeLinejoin: \"round\"\n};\n\nexport { defaultAttributes as default };\n//# sourceMappingURL=defaultAttributes.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nconst hasA11yProp = (props) => {\n for (const prop in props) {\n if (prop.startsWith(\"aria-\") || prop === \"role\" || prop === \"title\") {\n return true;\n }\n }\n return false;\n};\n\nexport { hasA11yProp };\n//# sourceMappingURL=hasA11yProp.mjs.map\n","\"use strict\";\n\"use client\";\n/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { createContext, useContext, useMemo, createElement } from 'react';\n\nconst LucideContext = createContext({});\nfunction LucideProvider({\n children,\n size,\n color,\n strokeWidth,\n absoluteStrokeWidth,\n className\n}) {\n const value = useMemo(\n () => ({\n size,\n color,\n strokeWidth,\n absoluteStrokeWidth,\n className\n }),\n [size, color, strokeWidth, absoluteStrokeWidth, className]\n );\n return createElement(LucideContext.Provider, { value }, children);\n}\nconst useLucideContext = () => useContext(LucideContext);\n\nexport { LucideProvider, useLucideContext };\n//# sourceMappingURL=context.mjs.map\n","\"use strict\";\n\"use client\";\n/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport defaultAttributes from './defaultAttributes.mjs';\nimport { hasA11yProp } from './shared/src/utils/hasA11yProp.mjs';\nimport { mergeClasses } from './shared/src/utils/mergeClasses.mjs';\nimport { useLucideContext } from './context.mjs';\n\nconst Icon = forwardRef(\n ({ color, size, strokeWidth, absoluteStrokeWidth, className = \"\", children, iconNode, ...rest }, ref) => {\n const {\n size: contextSize = 24,\n strokeWidth: contextStrokeWidth = 2,\n absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,\n color: contextColor = \"currentColor\",\n className: contextClass = \"\"\n } = useLucideContext() ?? {};\n const calculatedStrokeWidth = absoluteStrokeWidth ?? contextAbsoluteStrokeWidth ? Number(strokeWidth ?? contextStrokeWidth) * 24 / Number(size ?? contextSize) : strokeWidth ?? contextStrokeWidth;\n return createElement(\n \"svg\",\n {\n ref,\n ...defaultAttributes,\n width: size ?? contextSize ?? defaultAttributes.width,\n height: size ?? contextSize ?? defaultAttributes.height,\n stroke: color ?? contextColor,\n strokeWidth: calculatedStrokeWidth,\n className: mergeClasses(\"lucide\", contextClass, className),\n ...!children && !hasA11yProp(rest) && { \"aria-hidden\": \"true\" },\n ...rest\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...Array.isArray(children) ? children : [children]\n ]\n );\n }\n);\n\nexport { Icon as default };\n//# sourceMappingURL=Icon.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport { forwardRef, createElement } from 'react';\nimport { mergeClasses } from './shared/src/utils/mergeClasses.mjs';\nimport { toKebabCase } from './shared/src/utils/toKebabCase.mjs';\nimport { toPascalCase } from './shared/src/utils/toPascalCase.mjs';\nimport Icon from './Icon.mjs';\n\nconst createLucideIcon = (iconName, iconNode) => {\n const Component = forwardRef(\n ({ className, ...props }, ref) => createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className\n ),\n ...props\n })\n );\n Component.displayName = toPascalCase(iconName);\n return Component;\n};\n\nexport { createLucideIcon as default };\n//# sourceMappingURL=createLucideIcon.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49\",\n key: \"ct8e1f\"\n }\n ],\n [\"path\", { d: \"M14.084 14.158a3 3 0 0 1-4.242-4.242\", key: \"151rxh\" }],\n [\n \"path\",\n {\n d: \"M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143\",\n key: \"13bj9a\"\n }\n ],\n [\"path\", { d: \"m2 2 20 20\", key: \"1ooewy\" }]\n];\nconst EyeOff = createLucideIcon(\"eye-off\", __iconNode);\n\nexport { __iconNode, EyeOff as default };\n//# sourceMappingURL=eye-off.mjs.map\n","/**\n * @license lucide-react v1.22.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0\",\n key: \"1nclc0\"\n }\n ],\n [\"circle\", { cx: \"12\", cy: \"12\", r: \"3\", key: \"1v7zrd\" }]\n];\nconst Eye = createLucideIcon(\"eye\", __iconNode);\n\nexport { __iconNode, Eye as default };\n//# sourceMappingURL=eye.mjs.map\n","import { useState } from \"react\"\nimport { Eye, EyeOff } from \"lucide-react\"\nimport { useFormContext } from \"@rpcbase/form\"\n\n\ntype PasswordInputProps = {\n id: string\n name?: string\n className?: string\n placeholder?: string\n autoComplete?: string\n}\n\nexport const PasswordInput = ({\n id,\n name = \"password\",\n className,\n placeholder,\n autoComplete = \"current-password\",\n}: PasswordInputProps) => {\n const [showPassword, setShowPassword] = useState(false)\n const { register, formState } = useFormContext()\n\n const fieldError = formState.errors[name as keyof typeof formState.errors]\n const errorMessage =\n typeof fieldError === \"string\"\n ? fieldError\n : typeof fieldError?.message === \"string\"\n ? fieldError.message\n : undefined\n\n const inputClassName = className ? `${className} pr-11` : \"pr-11\"\n\n return (\n <>\n <div className=\"relative\">\n <input\n id={id}\n type={showPassword ? \"text\" : \"password\"}\n autoComplete={autoComplete}\n className={inputClassName}\n placeholder={placeholder}\n {...register(name as Parameters<typeof register>[0])}\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500\"\n onClick={() => setShowPassword((prev) => !prev)}\n aria-label={showPassword ? \"Hide characters\" : \"Show characters\"}\n >\n {showPassword ? (\n <EyeOff className=\"h-5 w-5\" aria-hidden />\n ) : (\n <Eye className=\"h-5 w-5\" aria-hidden />\n )}\n </button>\n </div>\n {errorMessage ? (\n <p className=\"mt-1 -mb-2 text-sm/6 text-red-500\">{errorMessage}</p>\n ) : null}\n </>\n )\n}\n","import {useEffect, useState} from \"react\"\nimport clsx from \"clsx\"\n\n\nexport const useResendCountdown = (seconds = 60, initiallyCountingDown = true) => {\n const [remaining, setRemaining] = useState(seconds)\n const [isCountingDown, setIsCountingDown] = useState(initiallyCountingDown)\n\n useEffect(() => {\n if (!isCountingDown) return\n\n const timer = setInterval(() => {\n setRemaining((prev) => {\n if (prev <= 1) {\n setIsCountingDown(false)\n return seconds\n }\n return prev - 1\n })\n }, 1000)\n\n return () => clearInterval(timer)\n }, [isCountingDown, seconds])\n\n const restart = (nextSeconds?: number) => {\n const value = typeof nextSeconds === \"number\" ? nextSeconds : seconds\n setRemaining(value)\n setIsCountingDown(true)\n }\n\n const minutes = Math.floor(remaining / 60)\n const secs = remaining % 60\n const formatted = `${minutes}:${secs.toString().padStart(2, \"0\")}`\n\n const canResend = !isCountingDown\n\n return {remaining, formatted, isCountingDown, canResend, restart}\n}\n\n\nexport const ResendCodeButton = ({\n seconds = 60,\n onResend,\n className,\n disabled,\n}: {\n seconds?: number\n onResend?: () => Promise<void> | void\n className?: string\n disabled?: boolean\n}) => {\n const [isSending, setIsSending] = useState(false)\n const {formatted, isCountingDown, restart, canResend} = useResendCountdown(seconds)\n\n const handleClick = async () => {\n if (!canResend || isSending || disabled) return\n\n try {\n setIsSending(true)\n if (onResend) {\n await onResend()\n }\n } finally {\n restart()\n setIsSending(false)\n }\n }\n\n const isDisabled = disabled || isCountingDown || isSending\n const showCountdown = isDisabled && (isCountingDown || isSending)\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={isDisabled}\n aria-label=\"Resend code\"\n className={clsx(\n \"text-sm font-semibold text-sky-600 hover:underline disabled:cursor-not-allowed disabled:text-gray-400\",\n className\n )}\n >\n Resend code\n </button>\n {showCountdown ? (\n <span\n data-testid=\"resend-countdown\"\n className=\"text-sm text-gray-500\"\n aria-live=\"polite\"\n >\n ({formatted})\n </span>\n ) : null}\n </span>\n )\n}\n","import { useRef, useState } from \"react\"\n\nimport { useResendCountdown } from \"./components/ResendCodeButton\"\n\n\nexport const DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS = 60\n\nexport type PasswordResetCodeRequestResult = {\n retryAfterSeconds: number\n}\n\nexport class PasswordResetCodeRequestError extends Error {\n retryAfterSeconds?: number\n errorCode?: string\n\n constructor(retryAfterSeconds?: number, errorCode?: string) {\n super(\"Unable to request a password reset code\")\n this.name = \"PasswordResetCodeRequestError\"\n this.retryAfterSeconds = retryAfterSeconds\n this.errorCode = errorCode\n }\n}\n\nconst parseRetryAfterSeconds = (value: unknown) => {\n const seconds = typeof value === \"number\" ? value : Number(value)\n if (!Number.isFinite(seconds) || seconds <= 0) return undefined\n return Math.ceil(seconds)\n}\n\nexport const requestPasswordResetCode = async(email: string): Promise<PasswordResetCodeRequestResult> => {\n const response = await fetch(\"/api/rb/auth/request-password-reset\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ email }),\n })\n\n const payload = await response.json().catch(() => null) as {\n success?: unknown\n error?: unknown\n retryAfterSeconds?: unknown\n } | null\n const retryAfterSeconds = parseRetryAfterSeconds(payload?.retryAfterSeconds)\n ?? parseRetryAfterSeconds(response.headers.get(\"Retry-After\"))\n\n if (!response.ok || payload?.success !== true) {\n const errorCode = typeof payload?.error === \"string\" ? payload.error : undefined\n throw new PasswordResetCodeRequestError(retryAfterSeconds, errorCode)\n }\n\n return {\n retryAfterSeconds: retryAfterSeconds ?? DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS,\n }\n}\n\nexport const usePasswordResetResend = ({\n email,\n cooldownSeconds = DEFAULT_PASSWORD_RESET_COOLDOWN_SECONDS,\n}: {\n email?: string | null\n cooldownSeconds?: number\n}) => {\n const countdown = useResendCountdown(cooldownSeconds)\n const [isResending, setIsResending] = useState(false)\n const isResendingRef = useRef(false)\n\n const resend = async() => {\n if (!email || !countdown.canResend || isResendingRef.current) return null\n\n isResendingRef.current = true\n setIsResending(true)\n\n try {\n const result = await requestPasswordResetCode(email)\n countdown.restart(result.retryAfterSeconds)\n return result\n } catch (error) {\n if (error instanceof PasswordResetCodeRequestError && error.retryAfterSeconds) {\n countdown.restart(error.retryAfterSeconds)\n }\n throw error\n } finally {\n isResendingRef.current = false\n setIsResending(false)\n }\n }\n\n return {\n resend,\n isResending,\n remainingSeconds: countdown.remaining,\n formatted: countdown.formatted,\n isCountingDown: countdown.isCountingDown,\n canResend: Boolean(email) && countdown.canResend && !isResending,\n }\n}\n"],"x_google_ignoreList":[7,8,9,10,11,12,13,14,15,16,17],"mappings":";;;;;;;;;;AAIA,IAAMG,wBAAwB;CAC5B,iBAAiB;EACf,SAAS;EACT,YAAY;CACd;CACA,iBAAiB;EACf,SAAS;EACT,YAAY;CACd;CACA,wBAAwB;EACtB,SAAS;EACT,YAAY;CACd;AACF;AAIA,IAAaE,cAAaC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAC,MAAA,EAAAC,WAAAC,IAAAC,aAAAL;CACzB,MAAAG,YAAAC,OAAAE,KAAAA,IAAA,OAAAF;CAMA,MAAAG,WAAiBX,YAAY;CAE7B,MAAAY,YAAkBX,sBAAsBU,SAAQE,SAA6B,EAAAC;CAC7E,MAAAC,eAAqBd,sBAAsBU,SAAQE,SAAgC,EAAAF;CAAA,IAAAK;CAAA,IAAAX,EAAA,OAAAU,gBAAAV,EAAA,OAAAO,WAAA;EAK9EI,KAAAJ,aAAAG,eACC,oBAAC,MAAD;GACY,WAAA;GACNA,IAAAA;aAEHH;EAJE,CAAA,IADN;EAOOP,EAAA,KAAAU;EAAAV,EAAA,KAAAO;EAAAP,EAAA,KAAAW;CAAA,OAAAA,KAAAX,EAAA;CAAA,IAAAY;CAAA,IAAAZ,EAAA,OAAAE,WAAA;EACRU,KAAA,oBAAA,OAAA;GAAe,WAAA;aACZV;EACG,CAAA;EAAAF,EAAA,KAAAE;EAAAF,EAAA,KAAAY;CAAA,OAAAA,KAAAZ,EAAA;CAAA,IAAAa;CAAA,IAAAb,EAAA,OAAAI,UAAA;EACNS,KAAA,oBAAA,OAAA;GAAe,WAAA;GACZT;EACG,CAAA;EAAAJ,EAAA,KAAAI;EAAAJ,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAAA,IAAAc;CAAA,IAAAd,EAAA,OAAAW,MAAAX,EAAA,OAAAY,MAAAZ,EAAA,OAAAa,IAAA;EAfRC,KAAA,qBAAA,OAAA;GAAe,WAAA;aAAf;IAEGH;IAQDC;IAGAC;GAGI;;EAAAb,EAAA,KAAAW;EAAAX,EAAA,KAAAY;EAAAZ,EAAA,KAAAa;EAAAb,EAAA,MAAAc;CAAA,OAAAA,KAAAd,EAAA;CAAA,OAhBNc;AAgBM;;;AC/CV,IAAaE,qBAAoBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,CAAA;CAAC,MAAA,EAAAC,SAAAC,cAAAJ;CAMjC,IAAAK;CAAA,IAAAJ,EAAA,OAAAG,WAAA;EAKgBC,KAAAP,KAAK,4SASbM,SAAS;EAACH,EAAA,KAAAG;EAAAH,EAAA,KAAAI;CAAA,OAAAA,KAAAJ,EAAA;CAAA,IAAAK;CAAA,IAAAC;CAAA,IAAAN,EAAA,OAAAO,OAAAC,IAAA,2BAAA,GAAA;EAEbH,KAAA,oBAAA,OAAA;GACY,WAAA;GACJ,OAAA;GACE,SAAA;GACH,MAAA;aAEL,oBAAA,QAAA,EAAQ,GAAA,giBAA+hB,CAAA;EACniB,CAAA;EACNC,KAAA,oBAAA,QAAA,EAAA,UAAM,sBAA0B,CAAA;EAAAN,EAAA,KAAAK;EAAAL,EAAA,KAAAM;CAAA,OAAA;EAAAD,KAAAL,EAAA;EAAAM,KAAAN,EAAA;CAAA;CAAA,IAAAS;CAAA,IAAAT,EAAA,OAAAE,WAAAF,EAAA,OAAAI,IAAA;EArBlCK,KAAA,qBAAA,UAAA;GACWP,SAAAA;GACE,WAAAE;aAFb,CAaEC,IAQAC,EACO;;EAAAN,EAAA,KAAAE;EAAAF,EAAA,KAAAI;EAAAJ,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAA,OAtBTS;AAsBS;;;AC/Bb,IAAaE,cAAaC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAC,MAAA,EAAAC,IAAAC,WAAAC,gBAAAL;CASzB,MAAA,EAAAM,UAAAC,cAA8BT,eAAe;CAE7C,MAAAU,QAAcD,UAASE,OAAOC;CAC1BC,IAAAA;CACJ,IAAI,OAAOH,UAAU,UACnBG,eAAeH;MACV,IAAIA,SAAS,OAAOA,MAAKI,YAAa,UAC3CD,eAAeH,MAAKI;CACrB,IAAAC;CAAA,IAAAZ,EAAA,OAAAK,UAAA;EAWSO,KAAAP,SAAS,OAAO;EAACL,EAAA,KAAAK;EAAAL,EAAA,KAAAY;CAAA,OAAAA,KAAAZ,EAAA;CAAA,IAAAa;CAAA,IAAAb,EAAA,OAAAG,aAAAH,EAAA,OAAAE,MAAAF,EAAA,OAAAI,eAAAJ,EAAA,OAAAY,IAAA;EAPvBC,KAAA,oBAAA,SAAA;GACMX;GACC,MAAA;GACL,UAAA;GACa,cAAA;GACFC;GACEC;GAAW,GACpBQ;EAAiB,CAAA;EACrBZ,EAAA,KAAAG;EAAAH,EAAA,KAAAE;EAAAF,EAAA,KAAAI;EAAAJ,EAAA,KAAAY;EAAAZ,EAAA,KAAAa;CAAA,OAAAA,KAAAb,EAAA;CAAA,IAAAc;CAAA,IAAAd,EAAA,OAAAU,cAAA;EACDI,KAAAJ,eACC,oBAAA,KAAA;GAAa,WAAA;aAAqCA;EAAiB,CAAA,IADpE;EAEOV,EAAA,KAAAU;EAAAV,EAAA,KAAAc;CAAA,OAAAA,KAAAd,EAAA;CAAA,IAAAe;CAAA,IAAAf,EAAA,OAAAa,MAAAb,EAAA,QAAAc,IAAA;EAZVC,KAAA,qBAAA,UAAA,EAAA,UAAA,CACEF,IASCC,EAEO,EAAA,CAAA;EACPd,EAAA,KAAAa;EAAAb,EAAA,MAAAc;EAAAd,EAAA,MAAAe;CAAA,OAAAA,KAAAf,EAAA;CAAA,OAbHe;AAaG;;;ACpCP,IAAMC,0BAA0BC,UAAkB;CAChD,KAAK,IAAIC,IAAI,GAAGA,IAAID,MAAME,QAAQD,KAAK;EACrC,MAAME,OAAOH,MAAMI,WAAWH,CAAC;EAC/B,IAAIE,SAAS,MAAMA,QAAQ,MAAMA,SAAS,KAAK,OAAO;CACxD;CACA,OAAO;AACT;AAEA,IAAaE,oBAAoBC,QAAuB;CACtD,IAAI,CAACA,KAAK,OAAO;CAEjB,MAAMC,WAAWD,IAAIE,KAAK;CAC1B,IAAI,CAACD,UAAU,OAAO;CAEtB,IAAI,CAACA,SAASE,WAAW,GAAG,KAAKF,SAASE,WAAW,IAAI,GACvD,OAAO;CAGT,IAAIV,uBAAuBQ,QAAQ,GACjC,OAAO;CAGT,IAAI;EACF,MAAMG,OAAO,IAAIC,IAAI,kBAAkB;EACvC,MAAMC,MAAM,IAAID,IAAIJ,UAAUG,IAAI;EAClC,IAAIE,IAAIC,WAAWH,KAAKG,QACtB,OAAO;EAET,OAAO,GAAGD,IAAIE,WAAWF,IAAIG,SAASH,IAAII;CAC5C,QAAQ;EACN,OAAO;CACT;AACF;;;ACpBA,IAAaa,cAAc,EACzBC,UACAC,gBAII;CACJ,MAAMC,WAAWd,YAAY;CAC7B,MAAM,CAACe,gBAAgBd,gBAAgB;CAEvC,MAAMe,UAAUd,QAA0B;EACxCe,eAAe;GACbC,OAAO;GACPC,UAAU;GACVC,YAAY;EACd;EACAC,iBAAiB;EACjBC,UAAUlB,YAAYG,aAAa;CACrC,CAAC;CAED,MAAMgB,WAA4C,OAAOC,SAAS;EAChER,QAAQS,YAAY,MAAM;EAE1B,IAAI;GACF,MAAMC,MAAM,MAAMC,MAAM,wBAAwB;IAC9CC,QAAQ;IACRC,SAAS,EACP,gBAAgB,mBAClB;IACAC,MAAMC,KAAKC,UAAUR,IAAI;IACzBS,aAAa;GACf,CAAC;GAED,MAAMC,OAAO,MAAMR,IAAIQ,KAAK,CAAC,CAACC,YAAY,IAAI;GAE9C,IAAI,CAACT,IAAIY,MAAM,CAACJ,MAAME,SAAS;IAC7B,MAAMG,UAAUL,MAAMG,UAAU,wBAC5B,+BACA;IACJrB,QAAQwB,SAAS,QAAQ;KAAEC,MAAM;KAAUF;IAAQ,CAAC;IACpD;GACF;GAEA,MAAMG,WAAWlC,iBAAiBO,aAAa4B,IAAI,MAAM,CAAC,KAAK;GAC/D7B,SAAS4B,UAAU,EAAEE,SAAS,KAAK,CAAC;EACtC,SAASC,KAAK;GACZ7B,QAAQwB,SAAS,QAAQ;IAAEC,MAAM;IAAUF,SAAS;GAAmC,CAAC;EAC1F;CACF;CAEA,OACE,oBAAC,cAAD;EAAc,GAAIvB;YAChB,qBAAC,QAAD;GAAM,QAAO;GAAO,YAAA;GAAsBH;GAAW,UAAUG,QAAQ8B,aAAavB,QAAQ;aAA5F,CACGX,UACAI,QAAQ+B,UAAUC,OAAOC,MAAMV,WAC9B,oBAAC,KAAD;IAAG,WAAU;IAA4B,MAAK;cAC3CvB,QAAQ+B,UAAUC,OAAOC,KAAKV;GAC9B,CAAA,CAED;;CACM,CAAA;AAElB;;;AC9DA,IAAasB,cAAc,EACzBC,UACAC,WACAC,kBAKI;CAEJ,MAAMC,WAAWb,YAAY;CAE7B,MAAM,CAACc,eAAeC,oBAAoBhB,SAAwB,IAAI;CACtE,MAAMiB,UAAUf,QAA0B;EACxCgB,eAAe;GACbC,OAAO;GACPC,UAAU;GACVC,YAAY;EACd;EACAC,iBAAiB;EACjBC,UAAUnB,YAAYG,eAAa;CACrC,CAAC;CAED,MAAMiB,WAA4C,OAAOC,SAAS;EAChET,iBAAiB,IAAI;EACrBC,QAAQS,YAAY,MAAM;EAE1B,IAAI;GACF,MAAMC,MAAM,MAAMC,MAAM,wBAAwB;IAC9CC,QAAQ;IACRC,SAAS,EACP,gBAAgB,mBAClB;IACAC,MAAMC,KAAKC,UAAUR,IAAI;IACzBS,aAAa;GACf,CAAC;GAED,MAAMC,OAAO,MAAMR,IAAIQ,KAAK;GAE5B,IAAI,CAACR,IAAIS,IAAI;IACX,MAAMC,UAAUF,KAAKG,UAAU,gBAC3B,+CACAH,KAAKG,UAAU,qBACb,gDACA;IACNrB,QAAQsB,SAAS,QAAQ;KAAEC,MAAM;KAAUH;IAAQ,CAAC;IACpD;GACF;GAEA,IAAI,CAACF,KAAKM,SAAS;IACjBxB,QAAQsB,SAAS,QAAQ;KAAEC,MAAM;KAAUH,SAAS;IAAoC,CAAC;IACzF;GACF;GAEA,MAAMK,SAAS,IAAIC,gBAAgB,EAAExB,OAAOM,KAAKN,MAAM,CAAC;GACxD,IAAIN,aACF6B,OAAOE,IAAI,QAAQ/B,WAAW;GAEhCC,SAAS,qBAAqB4B,OAAOG,SAAS,GAAG;GACjD7B,iBAAiB,yDAAyD;EAC5E,SAAS8B,KAAK;GACZ7B,QAAQsB,SAAS,QAAQ;IAAEC,MAAM;IAAUH,SAAS;GAAmC,CAAC;EAC1F;CACF;CAEA,OACE,oBAAC,cAAD;EAAc,GAAIpB;YAChB,qBAAC,QAAD;GAAM,QAAO;GAAO,YAAA;GAAsBL;GAAW,UAAUK,QAAQ8B,aAAavB,QAAQ;aAA5F;IACGb;IACAM,QAAQ+B,UAAUC,OAAOC,MAAMb,WAC9B,oBAAC,KAAD;KAAG,WAAU;KAA4B,MAAK;eAC3CpB,QAAQ+B,UAAUC,OAAOC,KAAKb;IAC9B,CAAA;IAEJtB,iBACC,oBAAC,KAAD;KAAG,WAAU;KAA8B,MAAK;eAC7CA;IACA,CAAA;GAED;;CACM,CAAA;AAElB;;;AC1FA,IAAasC,sBAAqBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAC,MAAA,EAAAC,OAAAC,IAAAC,OAAAL;CAE7B,MAAAM,YAAAD,OAAAE,KAAAA,IAAA,UAAAF;CAKJ,MAAA,EAAAG,aAAmBV,eAAe;CAAC,IAAAW;CAAA,IAAAR,EAAA,OAAAO,UAAA;EAMzBC,KAAAD,SAAS,YAAY;EAACP,EAAA,KAAAO;EAAAP,EAAA,KAAAQ;CAAA,OAAAA,KAAAR,EAAA;CAAA,IAAAS;CAAA,IAAAT,EAAA,OAAAK,aAAAL,EAAA,OAAAQ,IAAA;EAF5BC,KAAA,oBAAC,WAAD;GACK,IAAA;GAAY,GACXD;EAAsB,CAAA;EAC1BR,EAAA,KAAAK;EAAAL,EAAA,KAAAQ;EAAAR,EAAA,KAAAS;CAAA,OAAAA,KAAAT,EAAA;CAAA,IAAAU;CAAA,IAAAV,EAAA,OAAAE,OAAA;EACFQ,KAAA,oBAAA,SAAA;GACW,SAAA;GACC,WAAA;aAETR;EACK,CAAA;EAAAF,EAAA,KAAAE;EAAAF,EAAA,KAAAU;CAAA,OAAAA,KAAAV,EAAA;CAAA,IAAAW;CAAA,IAAAX,EAAA,OAAAS,MAAAT,EAAA,OAAAU,IAAA;EAVVC,KAAA,qBAAA,UAAA,EAAA,UAAA,CACEF,IAIAC,EAKQ,EAAA,CAAA;EACPV,EAAA,KAAAS;EAAAT,EAAA,KAAAU;EAAAV,EAAA,KAAAW;CAAA,OAAAA,KAAAX,EAAA;CAAA,OAXHW;AAWG;;;;;;;;;AClBP,IAAM,gBAAgB,GAAG,YAAY,QAAQ,QAAQ,WAAW,OAAO,UAAU;CAC/E,OAAO,QAAQ,SAAS,KAAK,UAAU,KAAK,MAAM,MAAM,MAAM,QAAQ,SAAS,MAAM;AACvF,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK;;;;;;;;;ACFlB,IAAM,eAAe,WAAW,OAAO,QAAQ,sBAAsB,OAAO,CAAC,CAAC,YAAY;;;;;;;;;ACA1F,IAAM,eAAe,WAAW,OAAO,QACrC,0BACC,OAAO,IAAI,OAAO,KAAK,GAAG,YAAY,IAAI,GAAG,YAAY,CAC5D;;;;;;;;;ACDA,IAAM,gBAAgB,WAAW;CAC/B,MAAM,YAAY,YAAY,MAAM;CACpC,OAAO,UAAU,OAAO,CAAC,CAAC,CAAC,YAAY,IAAI,UAAU,MAAM,CAAC;AAC9D;;;;;;;;;ACLA,IAAI,oBAAoB;CACtB,OAAO;CACP,OAAO;CACP,QAAQ;CACR,SAAS;CACT,MAAM;CACN,QAAQ;CACR,aAAa;CACb,eAAe;CACf,gBAAgB;AAClB;;;;;;;;;ACVA,IAAM,eAAe,UAAU;CAC7B,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,WAAW,OAAO,KAAK,SAAS,UAAU,SAAS,SAC1D,OAAO;CAGX,OAAO;AACT;;;;;;;;;ACHA,IAAM,gBAAgB,cAAc,CAAC,CAAC;AAqBtC,IAAM,yBAAyB,WAAW,aAAa;;;;;;;;;ACjBvD,IAAM,OAAO,YACV,EAAE,OAAO,MAAM,aAAa,qBAAqB,YAAY,IAAI,UAAU,UAAU,GAAG,QAAQ,QAAQ;CACvG,MAAM,EACJ,MAAM,cAAc,IACpB,aAAa,qBAAqB,GAClC,qBAAqB,6BAA6B,OAClD,OAAO,eAAe,gBACtB,WAAW,eAAe,OACxB,iBAAiB,KAAK,CAAC;CAC3B,MAAM,wBAAwB,uBAAuB,6BAA6B,OAAO,eAAe,kBAAkB,IAAI,KAAK,OAAO,QAAQ,WAAW,IAAI,eAAe;CAChL,OAAO,cACL,OACA;EACE;EACA,GAAG;EACH,OAAO,QAAQ,eAAe,kBAAkB;EAChD,QAAQ,QAAQ,eAAe,kBAAkB;EACjD,QAAQ,SAAS;EACjB,aAAa;EACb,WAAW,aAAa,UAAU,cAAc,SAAS;EACzD,GAAG,CAAC,YAAY,CAAC,YAAY,IAAI,KAAK,EAAE,eAAe,OAAO;EAC9D,GAAG;CACL,GACA,CACE,GAAG,SAAS,KAAK,CAAC,KAAK,WAAW,cAAc,KAAK,KAAK,CAAC,GAC3D,GAAG,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,CACnD,CACF;AACF,CACF;;;;;;;;;AC/BA,IAAM,oBAAoB,UAAU,aAAa;CAC/C,MAAM,YAAY,YACf,EAAE,WAAW,GAAG,SAAS,QAAQ,cAAc,MAAM;EACpD;EACA;EACA,WAAW,aACT,UAAU,YAAY,aAAa,QAAQ,CAAC,KAC5C,UAAU,YACV,SACF;EACA,GAAG;CACL,CAAC,CACH;CACA,UAAU,cAAc,aAAa,QAAQ;CAC7C,OAAO;AACT;;;;;;;ACDA,IAAM,SAAS,iBAAiB,WAAWC;CAjBzC,CACE,QACA;EACE,GAAG;EACH,KAAK;CACP,CACF;CACA,CAAC,QAAQ;EAAE,GAAG;EAAwC,KAAK;CAAS,CAAC;CACrE,CACE,QACA;EACE,GAAG;EACH,KAAK;CACP,CACF;CACA,CAAC,QAAQ;EAAE,GAAG;EAAc,KAAK;CAAS,CAAC;AAEFA,CAAU;;;;;;;ACRrD,IAAM,MAAM,iBAAiB,OAAO,CATlC,CACE,QACA;CACE,GAAG;CACH,KAAK;AACP,CACF,GACA,CAAC,UAAU;CAAE,IAAI;CAAM,IAAI;CAAM,GAAG;CAAK,KAAK;AAAS,CAAC,CAEtB,CAAU;;;ACN9C,IAAaW,iBAAgBC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAC,MAAA,EAAAR,IAAAC,MAAAQ,IAAAP,WAAAC,aAAAC,cAAAM,OAAAJ;CAE5B,MAAAL,OAAAQ,OAAAE,KAAAA,IAAA,aAAAF;CAGA,MAAAL,eAAAM,OAAAC,KAAAA,IAAA,qBAAAD;CAEA,MAAA,CAAAE,cAAAC,mBAAwClB,SAAS,KAAK;CACtD,MAAA,EAAAmB,UAAAC,cAAgCjB,eAAe;CAE/C,MAAAkB,aAAmBD,UAASE,OAAQhB;CACpC,MAAAiB,eACE,OAAOF,eAAe,WAAtBA,aAEI,OAAOA,YAAUG,YAAc,WAC7BH,WAAUG,UADZR,KAAAA;CAIN,MAAAS,iBAAuBlB,YAAA,GAAeA,UAAS,UAAxB;CAOT,MAAAmB,KAAAT,eAAA,SAAA;CAIO,MAAAU,KAAArB;CAAsC,IAAAuB;CAAA,IAAAjB,EAAA,OAAAO,YAAAP,EAAA,OAAAe,IAAA;EAA/CE,KAAAV,SAASQ,EAAsC;EAACf,EAAA,KAAAO;EAAAP,EAAA,KAAAe;EAAAf,EAAA,KAAAiB;CAAA,OAAAA,KAAAjB,EAAA;CAAA,IAAAkB;CAAA,IAAAlB,EAAA,OAAAH,gBAAAG,EAAA,OAAAP,MAAAO,EAAA,OAAAa,kBAAAb,EAAA,OAAAJ,eAAAI,EAAA,OAAAc,MAAAd,EAAA,OAAAiB,IAAA;EANtDC,KAAA,oBAAA,SAAA;GACMzB;GACE,MAAAqB;GACQjB;GACHgB,WAAAA;GACEjB;GAAW,GACpBqB;EAAgD,CAAA;EACpDjB,EAAA,KAAAH;EAAAG,EAAA,KAAAP;EAAAO,EAAA,KAAAa;EAAAb,EAAA,KAAAJ;EAAAI,EAAA,KAAAc;EAAAd,EAAA,KAAAiB;EAAAjB,EAAA,KAAAkB;CAAA,OAAAA,KAAAlB,EAAA;CAAA,IAAAmB;CAAA,IAAAnB,EAAA,QAAAoB,OAAAC,IAAA,2BAAA,GAAA;EAISF,WAAMb,gBAAgBgB,KAAe;EAACtB,EAAA,MAAAmB;CAAA,OAAAA,KAAAnB,EAAA;CACnC,MAAAuB,KAAAlB,eAAA,oBAAA;CAAoD,IAAAmB;CAAA,IAAAxB,EAAA,QAAAK,cAAA;EAE/DmB,KAAAnB,eACC,oBAAC,QAAD;GAAkB,WAAA;GAAU,eAAA;EAAW,CAAA,IAEvC,oBAAC,KAAD;GAAe,WAAA;GAAU,eAAA;EAAW,CAAA;EACrCL,EAAA,MAAAK;EAAAL,EAAA,MAAAwB;CAAA,OAAAA,KAAAxB,EAAA;CAAA,IAAAyB;CAAA,IAAAzB,EAAA,QAAAuB,MAAAvB,EAAA,QAAAwB,IAAA;EAVHC,MAAA,oBAAA,UAAA;GACO,MAAA;GACK,WAAA;GACD,SAAAN;GACG,cAAAI;aAEXC;EAKM,CAAA;EAAAxB,EAAA,MAAAuB;EAAAvB,EAAA,MAAAwB;EAAAxB,EAAA,MAAAyB;CAAA,OAAAA,MAAAzB,EAAA;CAAA,IAAA0B;CAAA,IAAA1B,EAAA,QAAAyB,OAAAzB,EAAA,QAAAkB,IAAA;EApBXQ,MAAA,qBAAA,OAAA;GAAe,WAAA;aAAf,CACER,IAQAO,GAYI;;EAAAzB,EAAA,MAAAyB;EAAAzB,EAAA,MAAAkB;EAAAlB,EAAA,MAAA0B;CAAA,OAAAA,MAAA1B,EAAA;CAAA,IAAA2B;CAAA,IAAA3B,EAAA,QAAAW,cAAA;EACLgB,MAAAhB,eACC,oBAAA,KAAA;GAAa,WAAA;aAAqCA;EAAiB,CAAA,IADpE;EAEOX,EAAA,MAAAW;EAAAX,EAAA,MAAA2B;CAAA,OAAAA,MAAA3B,EAAA;CAAA,IAAA4B;CAAA,IAAA5B,EAAA,QAAA0B,OAAA1B,EAAA,QAAA2B,KAAA;EAzBVC,MAAA,qBAAA,UAAA,EAAA,UAAA,CACEF,KAsBCC,GAEO,EAAA,CAAA;EACP3B,EAAA,MAAA0B;EAAA1B,EAAA,MAAA2B;EAAA3B,EAAA,MAAA4B;CAAA,OAAAA,MAAA5B,EAAA;CAAA,OA1BH4B;AA0BG;AA/CsB,SAAAN,MAAAO,MAAA;CAAA,OAkCsB,CAACA;AAAI;;;AC3CxD,IAAaI,sBAAqBC,IAAAC,OAAA;CAAA,MAAAC,IAAAC,EAAA,EAAA;CAAC,MAAAC,UAAAJ,OAAAK,KAAAA,IAAA,KAAAL;CAAc,MAAAM,wBAAAL,OAAAI,KAAAA,IAAA,OAAAJ;CAC/C,MAAA,CAAAM,WAAAC,gBAAkCX,SAASO,OAAO;CAClD,MAAA,CAAAK,gBAAAC,qBAA4Cb,SAASS,qBAAqB;CAAC,IAAAK;CAAA,IAAAC;CAAA,IAAAV,EAAA,OAAAO,kBAAAP,EAAA,OAAAE,SAAA;EAEjEO,WAAA;GACR,IAAI,CAACF,gBAAc;GAEnB,MAAAI,QAAcC,kBAAY;IACxBN,cAAaO,SAAA;KACX,IAAIA,QAAQ,GAAC;MACXL,kBAAkB,KAAK;MAAC,OACjBN;KAAO;KACf,OACMW,OAAO;IAAC,CAChB;GAAC,GACD,GAAI;GAAC,aAEKC,cAAcH,KAAK;EAAC;EAChCD,KAAA,CAACH,gBAAgBL,OAAO;EAACF,EAAA,KAAAO;EAAAP,EAAA,KAAAE;EAAAF,EAAA,KAAAS;EAAAT,EAAA,KAAAU;CAAA,OAAA;EAAAD,KAAAT,EAAA;EAAAU,KAAAV,EAAA;CAAA;CAd5BN,UAAUe,IAcPC,EAAyB;CAAC,IAAAK;CAAA,IAAAf,EAAA,OAAAE,SAAA;EAEba,MAAAC,gBAAA;GAEdV,aADc,OAAOU,gBAAgB,WAAvBA,cAAAd,OACI;GAClBM,kBAAkB,IAAI;EAAC;EACxBR,EAAA,KAAAE;EAAAF,EAAA,KAAAe;CAAA,OAAAA,KAAAf,EAAA;CAJD,MAAAkB,UAAgBH;CAMhB,MAAAI,UAAgBC,KAAIC,MAAOhB,YAAY,EAAE;CACzC,MAAAiB,OAAajB,YAAY;CAAE,IAAAkB;CAAA,IAAAvB,EAAA,OAAAsB,MAAA;EACKC,KAAAD,KAAIE,SAAU,CAAC,CAAAC,SAAU,GAAG,GAAG;EAACzB,EAAA,KAAAsB;EAAAtB,EAAA,KAAAuB;CAAA,OAAAA,KAAAvB,EAAA;CAAhE,MAAA0B,YAAkB,GAAGP,QAAO,GAAII;CAEhC,MAAAI,YAAkB,CAACpB;CAAc,IAAAqB;CAAA,IAAA5B,EAAA,OAAA2B,aAAA3B,EAAA,OAAA0B,aAAA1B,EAAA,QAAAO,kBAAAP,EAAA,QAAAK,aAAAL,EAAA,QAAAkB,SAAA;EAE1BU,KAAA;GAAAvB;GAAAqB;GAAAnB;GAAAoB;GAAAT;EAAyD;EAAClB,EAAA,KAAA2B;EAAA3B,EAAA,KAAA0B;EAAA1B,EAAA,MAAAO;EAAAP,EAAA,MAAAK;EAAAL,EAAA,MAAAkB;EAAAlB,EAAA,MAAA4B;CAAA,OAAAA,KAAA5B,EAAA;CAAA,OAA1D4B;AAA0D;AAInE,IAAaC,oBAAoB,EAC/B3B,UAAU,IACV4B,UACAC,WACAC,eAMI;CACJ,MAAM,CAACE,WAAWC,gBAAgBxC,SAAS,KAAK;CAChD,MAAM,EAAC+B,WAAWnB,gBAAgBW,SAASS,cAAa9B,mBAAmBK,OAAO;CAElF,MAAMkC,cAAc,YAAY;EAC9B,IAAI,CAACT,aAAaO,aAAaF,UAAU;EAEzC,IAAI;GACFG,aAAa,IAAI;GACjB,IAAIL,UACF,MAAMA,SAAS;EAEnB,UAAU;GACRZ,QAAQ;GACRiB,aAAa,KAAK;EACpB;CACF;CAEA,MAAME,aAAaL,YAAYzB,kBAAkB2B;CACjD,MAAMI,gBAAgBD,eAAe9B,kBAAkB2B;CAEvD,OACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,UAAD;GACE,MAAK;GACL,SAASE;GACT,UAAUC;GACV,cAAW;GACX,WAAWzC,KACT,yGACAmC,SACF;aAAE;EAGI,CAAA,GACPO,gBACC,qBAAC,QAAD;GACE,eAAY;GACZ,WAAU;GACV,aAAU;aAHZ;IAGoB;IAEhBZ;IAAU;GACR;OACJ,IACA;;AAEV;;;AC3FA,IAAagB,0CAA0C;AAMvD,IAAaG,gCAAb,cAAmDC,MAAM;CACvDF;CACAG;CAEAC,YAAYJ,mBAA4BG,WAAoB;EAC1D,MAAM,yCAAyC;EAC/C,KAAKE,OAAO;EACZ,KAAKL,oBAAoBA;EACzB,KAAKG,YAAYA;CACnB;AACF;AAEA,IAAMG,0BAA0BC,UAAmB;CACjD,MAAMC,UAAU,OAAOD,UAAU,WAAWA,QAAQE,OAAOF,KAAK;CAChE,IAAI,CAACE,OAAOC,SAASF,OAAO,KAAKA,WAAW,GAAG,OAAOG,KAAAA;CACtD,OAAOC,KAAKC,KAAKL,OAAO;AAC1B;AAEA,IAAaM,2BAA2B,OAAMC,UAA2D;CACvG,MAAME,WAAW,MAAMC,MAAM,uCAAuC;EAClEC,QAAQ;EACRC,SAAS,EAAE,gBAAgB,mBAAmB;EAC9CC,MAAMC,KAAKC,UAAU,EAAER,MAAM,CAAC;CAChC,CAAC;CAED,MAAMS,UAAU,MAAMP,SAASQ,KAAK,CAAC,CAACC,YAAY,IAAI;CAKtD,MAAM1B,oBAAoBM,uBAAuBkB,SAASxB,iBAAiB,KACtEM,uBAAuBW,SAASG,QAAQS,IAAI,aAAa,CAAC;CAE/D,IAAI,CAACZ,SAASa,MAAMN,SAASG,YAAY,MAEvC,MAAM,IAAI1B,8BAA8BD,mBADtB,OAAOwB,SAASI,UAAU,WAAWJ,QAAQI,QAAQjB,KAAAA,CACH;CAGtE,OAAO,EACLX,mBAAmBA,qBAAAA,GACrB;AACF;AAEA,IAAa+B,0BAA0B,EACrChB,OACAiB,kBAAAA,SAII;CACJ,MAAMC,YAAYpC,mBAAmBmC,eAAe;CACpD,MAAM,CAACE,aAAaC,kBAAkBvC,SAAS,KAAK;CACpD,MAAMwC,iBAAiBzC,OAAO,KAAK;CAEnC,MAAM0C,SAAS,YAAW;EACxB,IAAI,CAACtB,SAAS,CAACkB,UAAUK,aAAaF,eAAeG,SAAS,OAAO;EAErEH,eAAeG,UAAU;EACzBJ,eAAe,IAAI;EAEnB,IAAI;GACF,MAAMK,SAAS,MAAM1B,yBAAyBC,KAAK;GACnDkB,UAAUQ,QAAQD,OAAOxC,iBAAiB;GAC1C,OAAOwC;EACT,SAASZ,OAAO;GACd,IAAIA,iBAAiB3B,iCAAiC2B,MAAM5B,mBAC1DiC,UAAUQ,QAAQb,MAAM5B,iBAAiB;GAE3C,MAAM4B;EACR,UAAU;GACRQ,eAAeG,UAAU;GACzBJ,eAAe,KAAK;EACtB;CACF;CAEA,OAAO;EACLE;EACAH;EACAQ,kBAAkBT,UAAUU;EAC5BC,WAAWX,UAAUW;EACrBC,gBAAgBZ,UAAUY;EAC1BP,WAAWQ,QAAQ/B,KAAK,KAAKkB,UAAUK,aAAa,CAACJ;CACvD;AACF"}
@@ -4,7 +4,8 @@ export type PasswordResetCodeRequestResult = {
4
4
  };
5
5
  export declare class PasswordResetCodeRequestError extends Error {
6
6
  retryAfterSeconds?: number;
7
- constructor(retryAfterSeconds?: number);
7
+ errorCode?: string;
8
+ constructor(retryAfterSeconds?: number, errorCode?: string);
8
9
  }
9
10
  export declare const requestPasswordResetCode: (email: string) => Promise<PasswordResetCodeRequestResult>;
10
11
  export declare const usePasswordResetResend: ({ email, cooldownSeconds, }: {
@@ -1 +1 @@
1
- {"version":3,"file":"passwordReset.d.ts","sourceRoot":"","sources":["../src/passwordReset.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,uCAAuC,KAAK,CAAA;AAEzD,MAAM,MAAM,8BAA8B,GAAG;IAC3C,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAA;gBAEd,iBAAiB,CAAC,EAAE,MAAM;CAKvC;AAQD,eAAO,MAAM,wBAAwB,GAAS,OAAO,MAAM,KAAG,OAAO,CAAC,8BAA8B,CAqBnG,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,6BAGpC;IACD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;;;;;;;CAkCA,CAAA"}
1
+ {"version":3,"file":"passwordReset.d.ts","sourceRoot":"","sources":["../src/passwordReset.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,uCAAuC,KAAK,CAAA;AAEzD,MAAM,MAAM,8BAA8B,GAAG;IAC3C,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;gBAEN,iBAAiB,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAM3D;AAQD,eAAO,MAAM,wBAAwB,GAAS,OAAO,MAAM,KAAG,OAAO,CAAC,8BAA8B,CAuBnG,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,6BAGpC;IACD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;;;;;;;CAkCA,CAAA"}
package/dist/routes.js CHANGED
@@ -1,13 +1,13 @@
1
1
  //#region src/routes.ts
2
2
  var routes = Object.entries({ .../* #__PURE__ */ Object.assign({
3
3
  "./api/me/handler.ts": () => import("./handler-W_XZ0lnx.js"),
4
- "./api/request-password-reset/handler.ts": () => import("./handler-DOaODKMb.js"),
4
+ "./api/request-password-reset/handler.ts": () => import("./handler-aS6g_PVg.js"),
5
5
  "./api/resend-otp/handler.ts": () => import("./handler-C7SfLP4c.js"),
6
6
  "./api/set-active-account/handler.ts": () => import("./handler-J2FOxq-2.js"),
7
7
  "./api/set-new-password/handler.ts": () => import("./handler-DoyR_l2O.js"),
8
8
  "./api/sign-in/handler.ts": () => import("./handler-DH9FPTNh.js"),
9
9
  "./api/sign-out/handler.ts": () => import("./handler-DQvdRpG2.js"),
10
- "./api/sign-up/handler.ts": () => import("./handler-BerdJCZB.js"),
10
+ "./api/sign-up/handler.ts": () => import("./handler-B1ZE5nYO.js"),
11
11
  "./api/verify-otp/handler.ts": () => import("./handler-DAKI9QOQ.js"),
12
12
  "./api/verify-password-reset-otp/handler.ts": () => import("./handler-DAfIloZR.js")
13
13
  }) }).reduce((acc, [path, mod]) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpcbase/auth",
3
- "version": "0.186.0",
3
+ "version": "0.188.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -24,6 +24,11 @@
24
24
  "import": "./dist/oauth/index.js",
25
25
  "default": "./dist/oauth/index.js"
26
26
  },
27
+ "./email-validation": {
28
+ "types": "./dist/email-validation/index.d.ts",
29
+ "import": "./dist/email-validation/index.js",
30
+ "default": "./dist/email-validation/index.js"
31
+ },
27
32
  "./tailwind.source.css": "./tailwind.source.css"
28
33
  },
29
34
  "scripts": {
@@ -91,6 +96,9 @@
91
96
  }
92
97
  }
93
98
  },
99
+ "dependencies": {
100
+ "mailchecker": "6.0.21"
101
+ },
94
102
  "peerDependencies": {
95
103
  "@rpcbase/api": "*",
96
104
  "@rpcbase/db": "*",
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler-BerdJCZB.js","names":["crypto","Api","Ctx","ApiHandler","createProvisionedTenant","models","hashPasswordForStorage","sendEmail","AuthSessionUser","SignUp","signUp","RequestPayload","ResponsePayload","payload","ctx","Promise","User","getGlobal","Tenant","parsed","requestSchema","safeParse","success","res","status","error","email","password","rememberMe","_rememberMe","data","existingUser","findOne","console","log","hashedPassword","tenantId","randomUUID","emailVerificationCode","randomInt","toString","padStart","emailVerificationExpiresAt","Date","now","user","tenants","tenantRoles","save","to","subject","html","text","err","deleteOne","_id","rollbackErr","name","userId","api","post","ROUTE"],"sources":["../src/api/sign-up/handler.ts"],"sourcesContent":["import crypto from \"crypto\"\n\nimport { Api, Ctx, ApiHandler } from \"@rpcbase/api\"\nimport { createProvisionedTenant, models } from \"@rpcbase/db\"\nimport { hashPasswordForStorage, sendEmail } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SignUp from \"./index\"\n\n\nexport const signUp: ApiHandler<SignUp.RequestPayload, SignUp.ResponsePayload, AuthSessionUser> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SignUp.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n const Tenant = await models.getGlobal(\"RBTenant\", ctx)\n\n const parsed = SignUp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { email, password, rememberMe: _rememberMe } = parsed.data\n\n const existingUser = await User.findOne({ email })\n\n if (existingUser) {\n console.log(\"user with email already exists\", email)\n // For security, we don't want to reveal if an email is registered.\n // But for this implementation, we'll make it simple.\n ctx.res.status(409)\n return { success: false, error: \"user_exists\" }\n }\n\n const hashedPassword = await hashPasswordForStorage(password)\n\n const tenantId = crypto.randomUUID()\n\n const emailVerificationCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const emailVerificationExpiresAt = new Date(Date.now() + 10 * 60 * 1000)\n\n const user = new User({\n email,\n password: hashedPassword,\n tenants: [tenantId],\n tenantRoles: {\n [tenantId]: [\"owner\"],\n },\n emailVerificationCode,\n emailVerificationExpiresAt,\n })\n await user.save()\n\n try {\n await sendEmail({\n to: email,\n subject: `Verify your email: ${emailVerificationCode}`,\n html: `\n <p>Welcome to rpcbase!</p>\n <p>Your verification code is <strong>${emailVerificationCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Welcome to rpcbase! Your verification code is ${emailVerificationCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.error(\"failed to send sign-up email\", err)\n\n try {\n await User.deleteOne({ _id: user._id })\n } catch (rollbackErr) {\n console.error(\"failed to roll back user after sign-up email failure\", rollbackErr)\n }\n\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n try {\n await createProvisionedTenant(Tenant, {\n tenantId,\n name: email,\n })\n } catch (err) {\n console.error(\"failed to initialize tenant for user\", err)\n try {\n await User.deleteOne({ _id: user._id })\n } catch (rollbackErr) {\n console.error(\"failed to roll back user after tenant initialization failure\", rollbackErr)\n }\n ctx.res.status(500)\n return { success: false, error: \"tenant_initialization_failed\" }\n }\n\n console.log(\"created new user\", user._id.toString())\n\n return { success: true, userId: user._id.toString(), tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(SignUp.ROUTE, signUp)\n}\n"],"mappings":";;;;;AAWA,IAAaU,SAAqF,OAChGG,SACAC,QACoC;CACpC,MAAME,OAAO,MAAMX,OAAOY,UAAU,UAAUH,GAAG;CACjD,MAAMI,SAAS,MAAMb,OAAOY,UAAU,YAAYH,GAAG;CAErD,MAAMK,SAAAA,cAA8BE,UAAUR,OAAO;CAErD,IAAI,CAACM,OAAOG,SAAS;EACnBR,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAkB;CACpD;CAGA,MAAM,EAAEC,OAAOC,UAAUC,YAAYC,gBAAgBV,OAAOW;CAI5D,IAAIC,MAFuBf,KAAKgB,QAAQ,EAAEN,MAAM,CAAC,GAE/B;EAChBO,QAAQC,IAAI,kCAAkCR,KAAK;EAGnDZ,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAc;CAChD;CAEA,MAAMU,iBAAiB,MAAM7B,uBAAuBqB,QAAQ;CAE5D,MAAMS,WAAWpC,OAAOqC,WAAW;CAEnC,MAAMC,wBAAwBtC,OAAOuC,UAAU,GAAG,GAAS,CAAC,CAACC,SAAS,CAAC,CAACC,SAAS,GAAG,GAAG;CACvF,MAAMC,6BAA6B,IAAIC,KAAKA,KAAKC,IAAI,IAAI,MAAU,GAAI;CAEvE,MAAMC,OAAO,IAAI7B,KAAK;EACpBU;EACAC,UAAUQ;EACVW,SAAS,CAACV,QAAQ;EAClBW,aAAa,GACVX,WAAW,CAAC,OAAO,EACtB;EACAE;EACAI;CACF,CAAC;CACD,MAAMG,KAAKG,KAAK;CAEhB,IAAI;EACF,MAAMzC,UAAU;GACd0C,IAAIvB;GACJwB,SAAS,sBAAsBZ;GAC/Ba,MAAM;;+CAEmCb,sBAAqB;;;GAG9Dc,MAAM,iDAAiDd,sBAAqB;EAC9E,CAAC;CACH,SAASe,KAAK;EACZpB,QAAQR,MAAM,gCAAgC4B,GAAG;EAEjD,IAAI;GACF,MAAMrC,KAAKsC,UAAU,EAAEC,KAAKV,KAAKU,IAAI,CAAC;EACxC,SAASC,aAAa;GACpBvB,QAAQR,MAAM,wDAAwD+B,WAAW;EACnF;EAEA1C,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAAwB;CAC1D;CAEA,IAAI;EACF,MAAMrB,wBAAwBc,QAAQ;GACpCkB;GACAqB,MAAM/B;EACR,CAAC;CACH,SAAS2B,KAAK;EACZpB,QAAQR,MAAM,wCAAwC4B,GAAG;EACzD,IAAI;GACF,MAAMrC,KAAKsC,UAAU,EAAEC,KAAKV,KAAKU,IAAI,CAAC;EACxC,SAASC,aAAa;GACpBvB,QAAQR,MAAM,gEAAgE+B,WAAW;EAC3F;EACA1C,IAAIS,IAAIC,OAAO,GAAG;EAClB,OAAO;GAAEF,SAAS;GAAOG,OAAO;EAA+B;CACjE;CAEAQ,QAAQC,IAAI,oBAAoBW,KAAKU,IAAIf,SAAS,CAAC;CAEnD,OAAO;EAAElB,SAAS;EAAMoC,QAAQb,KAAKU,IAAIf,SAAS;EAAGJ;CAAS;AAChE;AAEA,IAAA,mBAAgBuB,QAA8B;CAC5CA,IAAIC,KAAKnD,OAAcC,MAAM;AAC/B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"handler-DOaODKMb.js","names":["z","ROUTE","requestSchema","object","email","string","RequestPayload","infer","responseSchema","success","boolean","error","optional","retryAfterSeconds","number","int","positive","ResponsePayload","crypto","Api","ApiHandler","Ctx","models","sendEmail","AuthSessionUser","RequestPasswordReset","PASSWORD_RESET_COOLDOWN_SECONDS","PASSWORD_RESET_CODE_TTL_SECONDS","maskEmailForLog","email","localRaw","domainRaw","split","local","domain","localPrefix","slice","Math","min","length","localMask","repeat","max","domainTailLength","domainTail","domainMask","requestPasswordReset","RequestPayload","ResponsePayload","payload","ctx","Promise","User","getGlobal","parsed","requestSchema","safeParse","success","res","status","error","data","requestedAt","Date","cooldownEligibleExpiresAt","getTime","passwordResetCode","randomInt","toString","padStart","passwordResetCodeExpiresAt","user","findOneAndUpdate","$or","$exists","$lte","$set","$unset","passwordResetToken","passwordResetTokenExpiresAt","returnDocument","setHeader","String","existingUser","findOne","_id","console","info","to","subject","html","text","err","retryAfterSeconds","releaseResult","updateOne","modifiedCount","removeHeader","releaseErr","api","post","ROUTE"],"sources":["../src/api/request-password-reset/index.ts","../src/api/request-password-reset/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/request-password-reset\"\n\nexport const requestSchema = z.object({\n email: z.string().email(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n retryAfterSeconds: z.number().int().positive().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { sendEmail } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as RequestPasswordReset from \"./index\"\n\n\nexport const PASSWORD_RESET_COOLDOWN_SECONDS = 60\nconst PASSWORD_RESET_CODE_TTL_SECONDS = 10 * 60\n\nconst maskEmailForLog = (email: string) => {\n const [localRaw, domainRaw = \"\"] = email.split(\"@\")\n const local = localRaw || \"\"\n const domain = domainRaw || \"\"\n const localPrefix = local.slice(0, Math.min(3, local.length))\n const localMask = \"*\".repeat(Math.max(5, local.length - localPrefix.length))\n\n if (!domain) {\n return `${localPrefix}${localMask}@****`\n }\n\n const domainTailLength = Math.min(7, domain.length)\n const domainTail = domain.slice(-domainTailLength)\n const domainMask = \"*\".repeat(Math.max(4, domain.length - domainTailLength))\n\n return `${localPrefix}${localMask}@${domainMask}${domainTail}`\n}\n\n\nexport const requestPasswordReset: ApiHandler<\n RequestPasswordReset.RequestPayload,\n RequestPasswordReset.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<RequestPasswordReset.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = RequestPasswordReset.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email } = parsed.data\n const requestedAt = new Date()\n const cooldownEligibleExpiresAt = new Date(\n requestedAt.getTime() + (PASSWORD_RESET_CODE_TTL_SECONDS - PASSWORD_RESET_COOLDOWN_SECONDS) * 1000,\n )\n const passwordResetCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const passwordResetCodeExpiresAt = new Date(requestedAt.getTime() + PASSWORD_RESET_CODE_TTL_SECONDS * 1000)\n\n const user = await User.findOneAndUpdate(\n {\n email,\n $or: [\n { passwordResetCodeExpiresAt: { $exists: false } },\n { passwordResetCodeExpiresAt: { $lte: cooldownEligibleExpiresAt } },\n ],\n },\n {\n $set: {\n passwordResetCode,\n passwordResetCodeExpiresAt,\n },\n $unset: {\n passwordResetToken: \"\",\n passwordResetTokenExpiresAt: \"\",\n },\n },\n { returnDocument: \"after\" },\n )\n\n ctx.res.setHeader(\"Retry-After\", String(PASSWORD_RESET_COOLDOWN_SECONDS))\n\n if (!user) {\n const existingUser = await User.findOne({ email }, { _id: 1 })\n\n if (!existingUser) {\n try {\n console.info(`sending password reset account notice to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: \"No account found for your password reset request\",\n html: `\n <p>Hello,</p>\n <p>You requested a new password reset code for this email address, but unfortunately we couldn't find an account associated with it.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n <p>If you're having trouble accessing your account, please contact support.</p>\n `,\n text: \"Hello, You requested a new password reset code for this email address, but unfortunately we couldn't find an account associated with it. If you didn't request this, you can ignore this message. If you're having trouble accessing your account, please contact support.\",\n })\n } catch (err) {\n console.error(\"failed to send password reset account notice email\", err)\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n }\n\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n }\n\n try {\n console.info(`sending password reset to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: `Your password reset code: ${passwordResetCode}`,\n html: `\n <p>Your password reset code is <strong>${passwordResetCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Your password reset code is ${passwordResetCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.error(\"failed to send password reset email\", err)\n\n try {\n const releaseResult = await User.updateOne(\n { _id: user._id, passwordResetCode },\n {\n $unset: {\n passwordResetCode: \"\",\n passwordResetCodeExpiresAt: \"\",\n },\n },\n )\n if (releaseResult.modifiedCount > 0) {\n ctx.res.removeHeader(\"Retry-After\")\n }\n } catch (releaseErr) {\n console.error(\"failed to release password reset cooldown\", releaseErr)\n }\n\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(RequestPasswordReset.ROUTE, requestPasswordReset)\n}\n"],"mappings":"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"}