better-auth 1.6.10 → 1.6.12

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.
Files changed (90) hide show
  1. package/dist/api/index.d.mts +8 -2
  2. package/dist/api/routes/callback.d.mts +1 -1
  3. package/dist/api/routes/callback.mjs +36 -40
  4. package/dist/api/routes/email-verification.d.mts +1 -0
  5. package/dist/api/routes/email-verification.mjs +4 -3
  6. package/dist/api/routes/session.mjs +14 -9
  7. package/dist/api/routes/sign-in.d.mts +1 -0
  8. package/dist/api/routes/sign-in.mjs +2 -1
  9. package/dist/api/routes/sign-up.d.mts +1 -0
  10. package/dist/api/routes/sign-up.mjs +9 -7
  11. package/dist/api/routes/update-user.mjs +5 -5
  12. package/dist/client/index.d.mts +2 -2
  13. package/dist/client/parser.mjs +0 -1
  14. package/dist/client/plugins/index.d.mts +3 -3
  15. package/dist/client/proxy.mjs +2 -1
  16. package/dist/context/helpers.mjs +3 -2
  17. package/dist/cookies/cookie-utils.d.mts +24 -1
  18. package/dist/cookies/cookie-utils.mjs +85 -22
  19. package/dist/cookies/index.d.mts +2 -3
  20. package/dist/cookies/index.mjs +39 -11
  21. package/dist/cookies/session-store.mjs +4 -23
  22. package/dist/db/get-migration.mjs +4 -4
  23. package/dist/db/index.d.mts +2 -2
  24. package/dist/db/index.mjs +3 -2
  25. package/dist/db/internal-adapter.mjs +96 -1
  26. package/dist/db/schema.d.mts +15 -2
  27. package/dist/db/schema.mjs +26 -1
  28. package/dist/db/with-hooks.d.mts +1 -0
  29. package/dist/db/with-hooks.mjs +58 -1
  30. package/dist/index.d.mts +2 -2
  31. package/dist/index.mjs +2 -2
  32. package/dist/oauth2/errors.mjs +16 -1
  33. package/dist/oauth2/link-account.mjs +6 -4
  34. package/dist/oauth2/state.mjs +8 -2
  35. package/dist/package.mjs +1 -1
  36. package/dist/plugins/access/access.d.mts +3 -15
  37. package/dist/plugins/access/access.mjs +11 -6
  38. package/dist/plugins/access/index.d.mts +2 -2
  39. package/dist/plugins/access/types.d.mts +11 -4
  40. package/dist/plugins/admin/access/statement.d.mts +29 -93
  41. package/dist/plugins/admin/admin.mjs +0 -4
  42. package/dist/plugins/admin/client.d.mts +1 -1
  43. package/dist/plugins/admin/routes.mjs +1 -0
  44. package/dist/plugins/anonymous/client.d.mts +1 -0
  45. package/dist/plugins/anonymous/error-codes.d.mts +1 -0
  46. package/dist/plugins/anonymous/error-codes.mjs +1 -0
  47. package/dist/plugins/anonymous/index.d.mts +1 -0
  48. package/dist/plugins/anonymous/index.mjs +16 -2
  49. package/dist/plugins/bearer/index.mjs +4 -9
  50. package/dist/plugins/captcha/index.mjs +2 -2
  51. package/dist/plugins/device-authorization/error-codes.mjs +1 -0
  52. package/dist/plugins/device-authorization/index.d.mts +1 -0
  53. package/dist/plugins/device-authorization/routes.mjs +34 -3
  54. package/dist/plugins/generic-oauth/index.d.mts +1 -1
  55. package/dist/plugins/generic-oauth/index.mjs +6 -6
  56. package/dist/plugins/generic-oauth/routes.mjs +34 -32
  57. package/dist/plugins/generic-oauth/types.d.mts +7 -0
  58. package/dist/plugins/index.d.mts +2 -2
  59. package/dist/plugins/last-login-method/client.mjs +2 -2
  60. package/dist/plugins/magic-link/index.d.mts +8 -1
  61. package/dist/plugins/magic-link/index.mjs +4 -17
  62. package/dist/plugins/mcp/authorize.mjs +8 -2
  63. package/dist/plugins/mcp/index.mjs +73 -34
  64. package/dist/plugins/multi-session/index.mjs +2 -2
  65. package/dist/plugins/oauth-proxy/index.mjs +44 -31
  66. package/dist/plugins/oauth-proxy/utils.mjs +3 -10
  67. package/dist/plugins/oidc-provider/authorize.mjs +8 -2
  68. package/dist/plugins/oidc-provider/index.mjs +63 -37
  69. package/dist/plugins/one-tap/index.mjs +13 -8
  70. package/dist/plugins/open-api/generator.mjs +16 -5
  71. package/dist/plugins/organization/access/statement.d.mts +68 -201
  72. package/dist/plugins/organization/adapter.mjs +61 -56
  73. package/dist/plugins/organization/client.d.mts +3 -1
  74. package/dist/plugins/organization/error-codes.d.mts +2 -0
  75. package/dist/plugins/organization/error-codes.mjs +3 -1
  76. package/dist/plugins/organization/routes/crud-access-control.d.mts +2 -2
  77. package/dist/plugins/organization/routes/crud-invites.mjs +7 -2
  78. package/dist/plugins/organization/types.d.mts +12 -2
  79. package/dist/plugins/two-factor/index.mjs +3 -2
  80. package/dist/plugins/username/index.d.mts +24 -2
  81. package/dist/plugins/username/index.mjs +49 -3
  82. package/dist/state.d.mts +2 -2
  83. package/dist/state.mjs +18 -4
  84. package/dist/test-utils/headers.mjs +2 -7
  85. package/dist/test-utils/test-instance.d.mts +25 -6
  86. package/dist/test-utils/test-instance.mjs +11 -2
  87. package/dist/utils/index.d.mts +1 -1
  88. package/dist/utils/url.d.mts +2 -1
  89. package/dist/utils/url.mjs +9 -3
  90. package/package.json +15 -14
@@ -228,7 +228,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
228
228
  allowedMediaTypes: string[];
229
229
  scope: "server";
230
230
  };
231
- }, void>;
231
+ }, never>;
232
232
  readonly getSession: better_call0.StrictEndpoint<"/get-session", {
233
233
  method: ("GET" | "POST")[];
234
234
  operationId: string;
@@ -327,6 +327,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
327
327
  callbackURL: zod.ZodOptional<zod.ZodString>;
328
328
  rememberMe: zod.ZodOptional<zod.ZodBoolean>;
329
329
  }, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
330
+ cloneRequest: true;
330
331
  metadata: {
331
332
  allowedMediaTypes: string[];
332
333
  $Infer: {
@@ -493,6 +494,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
493
494
  method: "POST";
494
495
  operationId: string;
495
496
  use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
497
+ cloneRequest: true;
496
498
  body: zod.ZodObject<{
497
499
  email: zod.ZodString;
498
500
  password: zod.ZodString;
@@ -724,6 +726,7 @@ declare function getEndpoints<Option extends BetterAuthOptions>(ctx: Awaitable<A
724
726
  readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
725
727
  method: "POST";
726
728
  operationId: string;
729
+ cloneRequest: true;
727
730
  body: zod.ZodObject<{
728
731
  email: zod.ZodEmail;
729
732
  callbackURL: zod.ZodOptional<zod.ZodString>;
@@ -2216,7 +2219,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2216
2219
  allowedMediaTypes: string[];
2217
2220
  scope: "server";
2218
2221
  };
2219
- }, void>;
2222
+ }, never>;
2220
2223
  readonly getSession: better_call0.StrictEndpoint<"/get-session", {
2221
2224
  method: ("GET" | "POST")[];
2222
2225
  operationId: string;
@@ -2315,6 +2318,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2315
2318
  callbackURL: zod.ZodOptional<zod.ZodString>;
2316
2319
  rememberMe: zod.ZodOptional<zod.ZodBoolean>;
2317
2320
  }, zod_v4_core0.$strip>, zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
2321
+ cloneRequest: true;
2318
2322
  metadata: {
2319
2323
  allowedMediaTypes: string[];
2320
2324
  $Infer: {
@@ -2481,6 +2485,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2481
2485
  method: "POST";
2482
2486
  operationId: string;
2483
2487
  use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
2488
+ cloneRequest: true;
2484
2489
  body: zod.ZodObject<{
2485
2490
  email: zod.ZodString;
2486
2491
  password: zod.ZodString;
@@ -2712,6 +2717,7 @@ declare const router: <Option extends BetterAuthOptions>(ctx: AuthContext, optio
2712
2717
  readonly sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
2713
2718
  method: "POST";
2714
2719
  operationId: string;
2720
+ cloneRequest: true;
2715
2721
  body: zod.ZodObject<{
2716
2722
  email: zod.ZodEmail;
2717
2723
  callbackURL: zod.ZodOptional<zod.ZodString>;
@@ -25,6 +25,6 @@ declare const callbackOAuth: better_call0.StrictEndpoint<"/callback/:id", {
25
25
  allowedMediaTypes: string[];
26
26
  scope: "server";
27
27
  };
28
- }, void>;
28
+ }, never>;
29
29
  //#endregion
30
30
  export { callbackOAuth };
@@ -1,6 +1,7 @@
1
+ import { isAPIError } from "../../utils/is-api-error.mjs";
1
2
  import { setSessionCookie } from "../../cookies/index.mjs";
2
3
  import { getAwaitableValue } from "../../context/helpers.mjs";
3
- import { missingEmailLogMessage } from "../../oauth2/errors.mjs";
4
+ import { missingEmailLogMessage, redirectOnError } from "../../oauth2/errors.mjs";
4
5
  import { parseState } from "../../oauth2/state.mjs";
5
6
  import { setTokenUtil } from "../../oauth2/utils.mjs";
6
7
  import { handleOAuthUserInfo } from "../../oauth2/link-account.mjs";
@@ -47,31 +48,20 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
47
48
  else throw new Error("Unsupported method");
48
49
  } catch (e) {
49
50
  c.context.logger.error("INVALID_CALLBACK_REQUEST", e);
50
- throw c.redirect(`${defaultErrorURL}?error=invalid_callback_request`);
51
- }
52
- const { code, error, state, error_description, device_id, user: userData } = queryOrBody;
53
- if (!state) {
54
- c.context.logger.error("State not found", error);
55
- const url = `${defaultErrorURL}${defaultErrorURL.includes("?") ? "&" : "?"}state=state_not_found`;
56
- throw c.redirect(url);
51
+ redirectOnError(c, defaultErrorURL, "invalid_callback_request");
57
52
  }
53
+ const { code, error, error_description, device_id, user: userData } = queryOrBody;
58
54
  const { codeVerifier, callbackURL, link, errorURL, newUserURL, requestSignUp } = await parseState(c);
59
- function redirectOnError(error, description) {
60
- const baseURL = errorURL ?? defaultErrorURL;
61
- const params = new URLSearchParams({ error });
62
- if (description) params.set("error_description", description);
63
- const url = `${baseURL}${baseURL.includes("?") ? "&" : "?"}${params.toString()}`;
64
- throw c.redirect(url);
65
- }
66
- if (error) redirectOnError(error, error_description);
55
+ const resolvedErrorURL = errorURL ?? defaultErrorURL;
56
+ if (error) redirectOnError(c, resolvedErrorURL, error, error_description);
67
57
  if (!code) {
68
58
  c.context.logger.error("Code not found");
69
- throw redirectOnError("no_code");
59
+ redirectOnError(c, resolvedErrorURL, "no_code");
70
60
  }
71
61
  const provider = await getAwaitableValue(c.context.socialProviders, { value: c.params.id });
72
62
  if (!provider) {
73
63
  c.context.logger.error("Oauth provider with id", c.params.id, "not found");
74
- throw redirectOnError("oauth_provider_not_found");
64
+ redirectOnError(c, resolvedErrorURL, "oauth_provider_not_found");
75
65
  }
76
66
  let tokens;
77
67
  try {
@@ -83,9 +73,9 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
83
73
  });
84
74
  } catch (e) {
85
75
  c.context.logger.error("", e);
86
- throw redirectOnError("invalid_code");
76
+ redirectOnError(c, resolvedErrorURL, "invalid_code");
87
77
  }
88
- if (!tokens) throw redirectOnError("invalid_code");
78
+ if (!tokens) redirectOnError(c, resolvedErrorURL, "invalid_code");
89
79
  const parsedUserData = userData ? safeJSONParse(userData) : null;
90
80
  const userInfo = await provider.getUserInfo({
91
81
  ...tokens,
@@ -93,22 +83,22 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
93
83
  }).then((res) => res?.user);
94
84
  if (!userInfo || userInfo.id === void 0 || userInfo.id === null) {
95
85
  c.context.logger.error("Unable to get user info");
96
- return redirectOnError("unable_to_get_user_info");
86
+ redirectOnError(c, resolvedErrorURL, "unable_to_get_user_info");
97
87
  }
98
88
  const providerAccountId = String(userInfo.id);
99
89
  if (!callbackURL) {
100
90
  c.context.logger.error("No callback URL found");
101
- throw redirectOnError("no_callback_url");
91
+ redirectOnError(c, resolvedErrorURL, "no_callback_url");
102
92
  }
103
93
  if (link) {
104
94
  if (!c.context.trustedProviders.includes(provider.id) && !userInfo.emailVerified || c.context.options.account?.accountLinking?.enabled === false) {
105
95
  c.context.logger.error("Unable to link account - untrusted provider");
106
- return redirectOnError("unable_to_link_account");
96
+ redirectOnError(c, resolvedErrorURL, "unable_to_link_account");
107
97
  }
108
- if (userInfo.email?.toLowerCase() !== link.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) return redirectOnError("email_doesn't_match");
98
+ if (userInfo.email?.toLowerCase() !== link.email.toLowerCase() && c.context.options.account?.accountLinking?.allowDifferentEmails !== true) redirectOnError(c, resolvedErrorURL, "email_doesn't_match");
109
99
  const existingAccount = await c.context.internalAdapter.findAccountByProviderId(providerAccountId, provider.id);
110
100
  if (existingAccount) {
111
- if (existingAccount.userId.toString() !== link.userId.toString()) return redirectOnError("account_already_linked_to_different_user");
101
+ if (existingAccount.userId.toString() !== link.userId.toString()) redirectOnError(c, resolvedErrorURL, "account_already_linked_to_different_user");
112
102
  const updateData = Object.fromEntries(Object.entries({
113
103
  accessToken: await setTokenUtil(tokens.accessToken, c.context),
114
104
  refreshToken: await setTokenUtil(tokens.refreshToken, c.context),
@@ -126,7 +116,7 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
126
116
  accessToken: await setTokenUtil(tokens.accessToken, c.context),
127
117
  refreshToken: await setTokenUtil(tokens.refreshToken, c.context),
128
118
  scope: tokens.scopes?.join(",")
129
- })) return redirectOnError("unable_to_link_account");
119
+ })) redirectOnError(c, resolvedErrorURL, "unable_to_link_account");
130
120
  let toRedirectTo;
131
121
  try {
132
122
  toRedirectTo = callbackURL.toString();
@@ -137,7 +127,7 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
137
127
  }
138
128
  if (!userInfo.email) {
139
129
  c.context.logger.error(missingEmailLogMessage(provider.id));
140
- return redirectOnError("email_not_found");
130
+ redirectOnError(c, resolvedErrorURL, "email_not_found");
141
131
  }
142
132
  const accountData = {
143
133
  providerId: provider.id,
@@ -145,21 +135,27 @@ const callbackOAuth = createAuthEndpoint("/callback/:id", {
145
135
  ...tokens,
146
136
  scope: tokens.scopes?.join(",")
147
137
  };
148
- const result = await handleOAuthUserInfo(c, {
149
- userInfo: {
150
- ...userInfo,
151
- id: providerAccountId,
152
- email: userInfo.email,
153
- name: userInfo.name || ""
154
- },
155
- account: accountData,
156
- callbackURL,
157
- disableSignUp: provider.disableImplicitSignUp && !requestSignUp || provider.options?.disableSignUp,
158
- overrideUserInfo: provider.options?.overrideUserInfoOnSignIn
159
- });
138
+ let result;
139
+ try {
140
+ result = await handleOAuthUserInfo(c, {
141
+ userInfo: {
142
+ ...userInfo,
143
+ id: providerAccountId,
144
+ email: userInfo.email,
145
+ name: userInfo.name || ""
146
+ },
147
+ account: accountData,
148
+ callbackURL,
149
+ disableSignUp: provider.disableImplicitSignUp && !requestSignUp || provider.options?.disableSignUp,
150
+ overrideUserInfo: provider.options?.overrideUserInfoOnSignIn
151
+ });
152
+ } catch (e) {
153
+ if (isAPIError(e) && e.body?.code) redirectOnError(c, resolvedErrorURL, e.body.code, e.body.message);
154
+ throw e;
155
+ }
160
156
  if (result.error) {
161
157
  c.context.logger.error(result.error.split(" ").join("_"));
162
- return redirectOnError(result.error.split(" ").join("_"));
158
+ redirectOnError(c, resolvedErrorURL, result.error.split(" ").join("_"));
163
159
  }
164
160
  const { session, user } = result.data;
165
161
  await setSessionCookie(c, {
@@ -27,6 +27,7 @@ declare function sendVerificationEmailFn(ctx: GenericEndpointContext, user: User
27
27
  declare const sendVerificationEmail: better_call0.StrictEndpoint<"/send-verification-email", {
28
28
  method: "POST";
29
29
  operationId: string;
30
+ cloneRequest: true;
30
31
  body: z.ZodObject<{
31
32
  email: z.ZodEmail;
32
33
  callbackURL: z.ZodOptional<z.ZodString>;
@@ -31,11 +31,12 @@ async function sendVerificationEmailFn(ctx, user) {
31
31
  user,
32
32
  url,
33
33
  token
34
- }, ctx.request));
34
+ }, ctx.request?.clone()));
35
35
  }
36
36
  const sendVerificationEmail = createAuthEndpoint("/send-verification-email", {
37
37
  method: "POST",
38
38
  operationId: "sendVerificationEmail",
39
+ cloneRequest: true,
39
40
  body: z.object({
40
41
  email: z.email().meta({ description: "The email to send the verification email to" }),
41
42
  callbackURL: z.string().meta({ description: "The URL to use for email verification callback" }).optional()
@@ -185,7 +186,7 @@ const verifyEmail = createAuthEndpoint("/verify-email", {
185
186
  },
186
187
  url,
187
188
  token: newToken
188
- }, ctx.request));
189
+ }, ctx.request?.clone()));
189
190
  if (ctx.query.callbackURL) throw ctx.redirect(ctx.query.callbackURL);
190
191
  return ctx.json({ status: true });
191
192
  }
@@ -238,7 +239,7 @@ const verifyEmail = createAuthEndpoint("/verify-email", {
238
239
  user: updatedUser,
239
240
  url: `${ctx.context.baseURL}/verify-email?token=${newToken}&callbackURL=${updateCallbackURL}`,
240
241
  token: newToken
241
- }, ctx.request));
242
+ }, ctx.request?.clone()));
242
243
  await setSessionCookie(ctx, {
243
244
  session: activeSession.session,
244
245
  user: {
@@ -129,12 +129,8 @@ const getSession = () => createAuthEndpoint("/get-session", {
129
129
  const updateAge = cookieRefreshCache.updateAge * 1e3;
130
130
  const shouldSkipSessionRefresh = await getShouldSkipSessionRefresh();
131
131
  if (timeUntilExpiry < updateAge && !shouldSkipSessionRefresh) {
132
- const newExpiresAt = getDate(ctx.context.options.session?.cookieCache?.maxAge || 300, "sec");
133
132
  const refreshedSession = {
134
- session: {
135
- ...session.session,
136
- expiresAt: newExpiresAt
137
- },
133
+ session: { ...session.session },
138
134
  user: session.user,
139
135
  updatedAt: Date.now()
140
136
  };
@@ -276,17 +272,26 @@ const getSessionFromCtx = async (ctx, config) => {
276
272
  method: "GET",
277
273
  asResponse: false,
278
274
  headers: ctx.headers,
279
- returnHeaders: false,
275
+ returnHeaders: true,
280
276
  returnStatus: false,
281
277
  query: {
282
278
  ...config,
283
279
  ...ctx.query
284
280
  }
285
- }).catch((e) => {
281
+ }).catch(() => {
286
282
  return null;
287
283
  });
288
- ctx.context.session = session;
289
- return session;
284
+ if (!session) {
285
+ ctx.context.session = null;
286
+ return null;
287
+ }
288
+ if (session.headers) session.headers.forEach((value, key) => {
289
+ if (!ctx.context.responseHeaders) ctx.context.responseHeaders = new Headers({ [key]: value });
290
+ else if (key.toLowerCase() === "set-cookie") ctx.context.responseHeaders.append(key, value);
291
+ else ctx.context.responseHeaders.set(key, value);
292
+ });
293
+ ctx.context.session = session.response;
294
+ return session.response;
290
295
  };
291
296
  /**
292
297
  * The middleware forces the endpoint to require a valid session.
@@ -114,6 +114,7 @@ declare const signInEmail: <O extends BetterAuthOptions>() => better_call0.Stric
114
114
  method: "POST";
115
115
  operationId: string;
116
116
  use: ((inputContext: better_call0.MiddlewareInputContext<better_call0.MiddlewareOptions>) => Promise<void>)[];
117
+ cloneRequest: true;
117
118
  body: z.ZodObject<{
118
119
  email: z.ZodString;
119
120
  password: z.ZodString;
@@ -144,6 +144,7 @@ const signInEmail = () => createAuthEndpoint("/sign-in/email", {
144
144
  method: "POST",
145
145
  operationId: "signInEmail",
146
146
  use: [formCsrfMiddleware],
147
+ cloneRequest: true,
147
148
  body: z.object({
148
149
  email: z.string().meta({ description: "Email of the user" }),
149
150
  password: z.string().meta({ description: "Password of the user" }),
@@ -236,7 +237,7 @@ const signInEmail = () => createAuthEndpoint("/sign-in/email", {
236
237
  user: user.user,
237
238
  url,
238
239
  token
239
- }, ctx.request));
240
+ }, ctx.request?.clone()));
240
241
  }
241
242
  throw APIError.from("FORBIDDEN", BASE_ERROR_CODES.EMAIL_NOT_VERIFIED);
242
243
  }
@@ -16,6 +16,7 @@ declare const signUpEmail: <O extends BetterAuthOptions>() => better_call0.Stric
16
16
  callbackURL: z.ZodOptional<z.ZodString>;
17
17
  rememberMe: z.ZodOptional<z.ZodBoolean>;
18
18
  }, z.core.$strip>, z.ZodRecord<z.ZodString, z.ZodAny>>;
19
+ cloneRequest: true;
19
20
  metadata: {
20
21
  allowedMediaTypes: string[];
21
22
  $Infer: {
@@ -1,6 +1,6 @@
1
1
  import { isAPIError } from "../../utils/is-api-error.mjs";
2
2
  import { formCsrfMiddleware } from "../middlewares/origin-check.mjs";
3
- import { parseUserInput, parseUserOutput } from "../../db/schema.mjs";
3
+ import { buildSyntheticUserOutput, parseUserInput, parseUserOutput } from "../../db/schema.mjs";
4
4
  import { setSessionCookie } from "../../cookies/index.mjs";
5
5
  import { createEmailVerificationToken } from "./email-verification.mjs";
6
6
  import { runWithTransaction } from "@better-auth/core/context";
@@ -23,6 +23,7 @@ const signUpEmail = () => createAuthEndpoint("/sign-up/email", {
23
23
  operationId: "signUpWithEmailAndPassword",
24
24
  use: [formCsrfMiddleware],
25
25
  body: signUpEmailBodySchema,
26
+ cloneRequest: true,
26
27
  metadata: {
27
28
  allowedMediaTypes: ["application/x-www-form-urlencoded", "application/json"],
28
29
  $Infer: {
@@ -170,14 +171,14 @@ const signUpEmail = () => createAuthEndpoint("/sign-up/email", {
170
171
  * between existing and non-existing emails.
171
172
  */
172
173
  await ctx.context.password.hash(password);
173
- if (ctx.context.options.emailAndPassword?.onExistingUserSignUp) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailAndPassword.onExistingUserSignUp({ user: dbUser.user }, ctx.request));
174
+ if (ctx.context.options.emailAndPassword?.onExistingUserSignUp) await ctx.context.runInBackgroundOrAwait(ctx.context.options.emailAndPassword.onExistingUserSignUp({ user: dbUser.user }, ctx.request?.clone()));
174
175
  const now = /* @__PURE__ */ new Date();
175
176
  const generatedId = ctx.context.generateId({ model: "user" }) || generateId();
176
177
  const coreFields = {
177
178
  name,
178
179
  email: normalizedEmail,
179
180
  emailVerified: false,
180
- image: image || null,
181
+ image: image ?? null,
181
182
  createdAt: now,
182
183
  updatedAt: now
183
184
  };
@@ -187,16 +188,17 @@ const signUpEmail = () => createAuthEndpoint("/sign-up/email", {
187
188
  const additionalFieldKeys = Object.keys(ctx.context.options.user?.additionalFields ?? {});
188
189
  const additionalFields = {};
189
190
  for (const key of additionalFieldKeys) if (key in additionalUserFields) additionalFields[key] = additionalUserFields[key];
190
- syntheticUser = customSyntheticUser({
191
+ const customResult = customSyntheticUser({
191
192
  coreFields,
192
193
  additionalFields,
193
194
  id: generatedId
194
195
  });
195
- } else syntheticUser = {
196
+ syntheticUser = buildSyntheticUserOutput(ctx.context.options, customResult);
197
+ } else syntheticUser = buildSyntheticUserOutput(ctx.context.options, {
196
198
  ...coreFields,
197
199
  ...additionalUserFields,
198
200
  id: generatedId
199
- };
201
+ });
200
202
  return ctx.json({
201
203
  token: null,
202
204
  user: parseUserOutput(ctx.context.options, syntheticUser)
@@ -244,7 +246,7 @@ const signUpEmail = () => createAuthEndpoint("/sign-up/email", {
244
246
  user: createdUser,
245
247
  url,
246
248
  token
247
- }, ctx.request));
249
+ }, ctx.request?.clone()));
248
250
  }
249
251
  if (shouldSkipAutoSignIn) return ctx.json({
250
252
  token: null,
@@ -410,7 +410,7 @@ const changeEmail = createAuthEndpoint("/change-email", {
410
410
  }, async (ctx) => {
411
411
  if (!ctx.context.options.user?.changeEmail?.enabled) {
412
412
  ctx.context.logger.error("Change email is disabled.");
413
- throw APIError.fromStatus("BAD_REQUEST", { message: "Change email is disabled" });
413
+ throw APIError.from("BAD_REQUEST", BASE_ERROR_CODES.CHANGE_EMAIL_DISABLED);
414
414
  }
415
415
  const newEmail = ctx.body.newEmail.toLowerCase();
416
416
  if (newEmail === ctx.context.session.user.email) {
@@ -424,8 +424,8 @@ const changeEmail = createAuthEndpoint("/change-email", {
424
424
  * email would later throw 400, leaking email existence.
425
425
  */
426
426
  const canUpdateWithoutVerification = ctx.context.session.user.emailVerified !== true && ctx.context.options.user.changeEmail.updateEmailWithoutVerification;
427
- const canSendConfirmation = ctx.context.session.user.emailVerified && ctx.context.options.user.changeEmail.sendChangeEmailConfirmation;
428
427
  const canSendVerification = ctx.context.options.emailVerification?.sendVerificationEmail;
428
+ const canSendConfirmation = canSendVerification && ctx.context.session.user.emailVerified && ctx.context.options.user.changeEmail.sendChangeEmailConfirmation;
429
429
  if (!canUpdateWithoutVerification && !canSendConfirmation && !canSendVerification) {
430
430
  ctx.context.logger.error("Verification email isn't enabled.");
431
431
  throw APIError.fromStatus("BAD_REQUEST", { message: "Verification email isn't enabled" });
@@ -449,7 +449,7 @@ const changeEmail = createAuthEndpoint("/change-email", {
449
449
  });
450
450
  if (canSendVerification) {
451
451
  const token = await createEmailVerificationToken(ctx.context.secret, newEmail, void 0, ctx.context.options.emailVerification?.expiresIn);
452
- const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
452
+ const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
453
453
  await ctx.context.runInBackgroundOrAwait(canSendVerification({
454
454
  user: {
455
455
  ...ctx.context.session.user,
@@ -466,7 +466,7 @@ const changeEmail = createAuthEndpoint("/change-email", {
466
466
  */
467
467
  if (canSendConfirmation) {
468
468
  const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-confirmation" });
469
- const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
469
+ const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
470
470
  await ctx.context.runInBackgroundOrAwait(canSendConfirmation({
471
471
  user: ctx.context.session.user,
472
472
  newEmail,
@@ -480,7 +480,7 @@ const changeEmail = createAuthEndpoint("/change-email", {
480
480
  throw APIError.fromStatus("BAD_REQUEST", { message: "Verification email isn't enabled" });
481
481
  }
482
482
  const token = await createEmailVerificationToken(ctx.context.secret, ctx.context.session.user.email, newEmail, ctx.context.options.emailVerification?.expiresIn, { requestType: "change-email-verification" });
483
- const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
483
+ const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${encodeURIComponent(ctx.body.callbackURL || "/")}`;
484
484
  await ctx.context.runInBackgroundOrAwait(canSendVerification({
485
485
  user: {
486
486
  ...ctx.context.session.user,
@@ -8,7 +8,7 @@ import { parseJSON } from "./parser.mjs";
8
8
  import { AuthQueryAtom, useAuthQuery } from "./query.mjs";
9
9
  import { SessionRefreshOptions, SessionResponse, createSessionRefreshManager } from "./session-refresh.mjs";
10
10
  import { AuthClient, createAuthClient } from "./vanilla.mjs";
11
- import { AccessControl, ArrayElement, Role, Statements, SubArray, Subset } from "../plugins/access/types.mjs";
11
+ import { AccessControl, ArrayElement, ExactRoleStatements, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, Statements, SubArray, Subset } from "../plugins/access/types.mjs";
12
12
  import { AuthorizeResponse, createAccessControl, role } from "../plugins/access/access.mjs";
13
13
  import { OrganizationOptions } from "../plugins/organization/types.mjs";
14
14
  import { InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferTeam, Invitation, InvitationInput, InvitationStatus, Member, MemberInput, Organization, OrganizationInput, OrganizationRole, OrganizationSchema, Team, TeamInput, TeamMember, TeamMemberInput, defaultRolesSchema, invitationSchema, invitationStatus, memberSchema, organizationRoleSchema, organizationSchema, roleSchema, teamMemberSchema, teamSchema } from "../plugins/organization/schema.mjs";
@@ -31,4 +31,4 @@ declare function InferAuth<O extends {
31
31
  options: BetterAuthOptions;
32
32
  }>(): O["options"];
33
33
  //#endregion
34
- export { AccessControl, ArrayElement, AuthClient, AuthQueryAtom, AuthorizeResponse, BetterAuthClientOptions, BetterAuthClientPlugin, BroadcastChannel, BroadcastListener, BroadcastMessage, CamelCase, ClientAtomListener, ClientStore, type DBPrimitive, DefaultOrganizationPlugin, DynamicAccessControlEndpoints, ExtractPluginField, type FocusListener, type FocusManager, HasRequiredKeys, InferActions, InferAdditionalFromClient, InferAuth, InferClientAPI, InferCtx, InferErrorCodes, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPlugin, InferPluginFieldFromTuple, InferRoute, InferRoutes, InferSessionFromClient, InferSignUpEmailCtx, InferTeam, InferUserFromClient, InferUserUpdateCtx, Invitation, InvitationInput, InvitationStatus, IsAny, IsSignal, Member, MemberInput, MergeRoutes, type OnlineListener, type OnlineManager, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, OverrideMerge, PathToObject, Prettify, PrettifyDeep, ProxyRequest, RequiredKeysOf, Role, SessionQueryParams, SessionRefreshOptions, SessionResponse, Statements, StripEmptyObjects, SubArray, Subset, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, type UnionToIntersection, createAccessControl, createAuthClient, createSessionRefreshManager, defaultRolesSchema, getGlobalBroadcastChannel, getOrgAdapter, hasPermission, invitationSchema, invitationStatus, kBroadcastChannel, kFocusManager, kOnlineManager, memberSchema, organization, organizationRoleSchema, organizationSchema, parseJSON, parseRoles, role, roleSchema, teamMemberSchema, teamSchema, useAuthQuery };
34
+ export { AccessControl, ArrayElement, AuthClient, AuthQueryAtom, AuthorizeResponse, BetterAuthClientOptions, BetterAuthClientPlugin, BroadcastChannel, BroadcastListener, BroadcastMessage, CamelCase, ClientAtomListener, ClientStore, type DBPrimitive, DefaultOrganizationPlugin, DynamicAccessControlEndpoints, ExactRoleStatements, ExtractPluginField, type FocusListener, type FocusManager, HasRequiredKeys, InferActions, InferAdditionalFromClient, InferAuth, InferClientAPI, InferCtx, InferErrorCodes, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPlugin, InferPluginFieldFromTuple, InferRoute, InferRoutes, InferSessionFromClient, InferSignUpEmailCtx, InferTeam, InferUserFromClient, InferUserUpdateCtx, Invitation, InvitationInput, InvitationStatus, IsAny, IsSignal, Member, MemberInput, MergeRoutes, type OnlineListener, type OnlineManager, Organization, OrganizationCreator, OrganizationEndpoints, OrganizationInput, OrganizationOptions, OrganizationPlugin, OrganizationRole, OrganizationSchema, OverrideMerge, PathToObject, Prettify, PrettifyDeep, ProxyRequest, RequiredKeysOf, Role, RoleAuthorizeRequest, RoleInput, RoleStatements, SessionQueryParams, SessionRefreshOptions, SessionResponse, Statements, StripEmptyObjects, SubArray, Subset, Team, TeamEndpoints, TeamInput, TeamMember, TeamMemberInput, type UnionToIntersection, createAccessControl, createAuthClient, createSessionRefreshManager, defaultRolesSchema, getGlobalBroadcastChannel, getOrgAdapter, hasPermission, invitationSchema, invitationStatus, kBroadcastChannel, kFocusManager, kOnlineManager, memberSchema, organization, organizationRoleSchema, organizationSchema, parseJSON, parseRoles, role, roleSchema, teamMemberSchema, teamSchema, useAuthQuery };
@@ -34,7 +34,6 @@ function betterJSONParse(value, options = {}) {
34
34
  const { strict = false, warnings = false, reviver, parseDates = true } = options;
35
35
  if (typeof value !== "string") return value;
36
36
  const trimmed = value.trim();
37
- if (trimmed.length > 0 && trimmed[0] === "\"" && trimmed.endsWith("\"") && !trimmed.slice(1, -1).includes("\"")) return trimmed.slice(1, -1);
38
37
  const lowerValue = trimmed.toLowerCase();
39
38
  if (lowerValue.length <= 9 && lowerValue in SPECIAL_VALUES) return SPECIAL_VALUES[lowerValue];
40
39
  if (!JSON_SIGNATURE.test(trimmed)) {
@@ -31,7 +31,7 @@ import { USERNAME_ERROR_CODES } from "../../plugins/username/error-codes.mjs";
31
31
  import { ORGANIZATION_ERROR_CODES } from "../../plugins/organization/error-codes.mjs";
32
32
  import { inferAdditionalFields } from "../../plugins/additional-fields/client.mjs";
33
33
  import { ADMIN_ERROR_CODES } from "../../plugins/admin/error-codes.mjs";
34
- import { adminClient } from "../../plugins/admin/client.mjs";
34
+ import { AdminClientOptions, adminClient } from "../../plugins/admin/client.mjs";
35
35
  import { ANONYMOUS_ERROR_CODES } from "../../plugins/anonymous/error-codes.mjs";
36
36
  import { anonymousClient } from "../../plugins/anonymous/client.mjs";
37
37
  import { customSessionClient } from "../../plugins/custom-session/client.mjs";
@@ -47,10 +47,10 @@ import { multiSessionClient } from "../../plugins/multi-session/client.mjs";
47
47
  import { OidcClientPlugin, oidcClient } from "../../plugins/oidc-provider/client.mjs";
48
48
  import { GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, oneTapClient } from "../../plugins/one-tap/client.mjs";
49
49
  import { oneTimeTokenClient } from "../../plugins/one-time-token/client.mjs";
50
- import { clientSideHasPermission, inferOrgAdditionalFields, organizationClient } from "../../plugins/organization/client.mjs";
50
+ import { OrganizationClientOptions, clientSideHasPermission, inferOrgAdditionalFields, organizationClient } from "../../plugins/organization/client.mjs";
51
51
  import { PHONE_NUMBER_ERROR_CODES } from "../../plugins/phone-number/error-codes.mjs";
52
52
  import { phoneNumberClient } from "../../plugins/phone-number/client.mjs";
53
53
  import { siweClient } from "../../plugins/siwe/client.mjs";
54
54
  import { usernameClient } from "../../plugins/username/client.mjs";
55
55
  import { InferServerPlugin } from "./infer-plugin.mjs";
56
- export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, AdminOptions, AnonymousOptions, AnonymousSession, Auth0Options, AuthorizationQuery, BackupCodeOptions, BaseOAuthProviderOptions, Client, CodeVerificationValue, EMAIL_OTP_ERROR_CODES, ExtractPluginField, type FieldAttributeToObject, GENERIC_OAUTH_ERROR_CODES, GenericOAuthConfig, GenericOAuthOptions, GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, GumroadOptions, HasRequiredKeys, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginFieldFromTuple, InferServerPlugin, InferTeam, Invitation, InvitationInput, InvitationStatus, IsAny, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodClientConfig, LineOptions, MULTI_SESSION_ERROR_CODES, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OIDCMetadata, OIDCOptions, ORGANIZATION_ERROR_CODES, OTPOptions, OidcClientPlugin, OktaOptions, OneTimeTokenOptions, Organization, OrganizationInput, OrganizationRole, OrganizationSchema, OverrideMerge, PHONE_NUMBER_ERROR_CODES, PatreonOptions, PhoneNumberOptions, Prettify, PrettifyDeep, type RemoveFieldsWithReturnedFalse, RequiredKeysOf, SessionWithImpersonatedBy, SlackOptions, StripEmptyObjects, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamInput, TeamMember, TeamMemberInput, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UnionToIntersection, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, adminClient, anonymousClient, auth0, backupCode2fa, clientSideHasPermission, customSessionClient, defaultRolesSchema, deviceAuthorizationClient, emailOTPClient, encodeBackupCodes, generateBackupCodes, genericOAuthClient, getBackupCodes, gumroad, hubspot, inferAdditionalFields, inferOrgAdditionalFields, invitationSchema, invitationStatus, jwtClient, keycloak, lastLoginMethodClient, line, magicLinkClient, memberSchema, microsoftEntraId, multiSessionClient, oidcClient, okta, oneTapClient, oneTimeTokenClient, organizationClient, organizationRoleSchema, organizationSchema, otp2fa, patreon, phoneNumberClient, roleSchema, schema, siweClient, slack, teamMemberSchema, teamSchema, totp2fa, twoFactorClient, usernameClient, verifyBackupCode };
56
+ export { ADMIN_ERROR_CODES, ANONYMOUS_ERROR_CODES, AdminClientOptions, AdminOptions, AnonymousOptions, AnonymousSession, Auth0Options, AuthorizationQuery, BackupCodeOptions, BaseOAuthProviderOptions, Client, CodeVerificationValue, EMAIL_OTP_ERROR_CODES, ExtractPluginField, type FieldAttributeToObject, GENERIC_OAUTH_ERROR_CODES, GenericOAuthConfig, GenericOAuthOptions, GoogleOneTapActionOptions, GoogleOneTapOptions, GsiButtonConfiguration, GumroadOptions, HasRequiredKeys, HubSpotOptions, InferAdminRolesFromOption, InferInvitation, InferMember, InferOrganization, InferOrganizationRolesFromOption, InferOrganizationZodRolesFromOption, InferPluginFieldFromTuple, InferServerPlugin, InferTeam, Invitation, InvitationInput, InvitationStatus, IsAny, JWKOptions, JWSAlgorithms, Jwk, JwtOptions, KeycloakOptions, LastLoginMethodClientConfig, LineOptions, MULTI_SESSION_ERROR_CODES, Member, MemberInput, MicrosoftEntraIdOptions, MultiSessionConfig, OAuthAccessToken, OIDCMetadata, OIDCOptions, ORGANIZATION_ERROR_CODES, OTPOptions, OidcClientPlugin, OktaOptions, OneTimeTokenOptions, Organization, OrganizationClientOptions, OrganizationInput, OrganizationRole, OrganizationSchema, OverrideMerge, PHONE_NUMBER_ERROR_CODES, PatreonOptions, PhoneNumberOptions, Prettify, PrettifyDeep, type RemoveFieldsWithReturnedFalse, RequiredKeysOf, SessionWithImpersonatedBy, SlackOptions, StripEmptyObjects, TOTPOptions, TWO_FACTOR_ERROR_CODES, Team, TeamInput, TeamMember, TeamMemberInput, TokenBody, TwoFactorOptions, TwoFactorProvider, TwoFactorTable, USERNAME_ERROR_CODES, UnionToIntersection, UserWithAnonymous, UserWithPhoneNumber, UserWithRole, UserWithTwoFactor, adminClient, anonymousClient, auth0, backupCode2fa, clientSideHasPermission, customSessionClient, defaultRolesSchema, deviceAuthorizationClient, emailOTPClient, encodeBackupCodes, generateBackupCodes, genericOAuthClient, getBackupCodes, gumroad, hubspot, inferAdditionalFields, inferOrgAdditionalFields, invitationSchema, invitationStatus, jwtClient, keycloak, lastLoginMethodClient, line, magicLinkClient, memberSchema, microsoftEntraId, multiSessionClient, oidcClient, okta, oneTapClient, oneTimeTokenClient, organizationClient, organizationRoleSchema, organizationSchema, otp2fa, patreon, phoneNumberClient, roleSchema, schema, siweClient, slack, teamMemberSchema, teamSchema, totp2fa, twoFactorClient, usernameClient, verifyBackupCode };
@@ -1,4 +1,5 @@
1
1
  import { isAtom } from "../utils/is-atom.mjs";
2
+ import { toKebabCase } from "@better-auth/core/utils/string";
2
3
  //#region src/client/proxy.ts
3
4
  function getMethod(path, knownPathMethods, args) {
4
5
  const method = knownPathMethods[path];
@@ -26,7 +27,7 @@ function createDynamicPathProxy(routes, client, knownPathMethods, atoms, atomLis
26
27
  return createProxy(fullPath);
27
28
  },
28
29
  apply: async (_, __, args) => {
29
- const routePath = "/" + path.map((segment) => segment.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)).join("/");
30
+ const routePath = "/" + path.map(toKebabCase).join("/");
30
31
  const arg = args[0] || {};
31
32
  const fetchOptions = args[1] || {};
32
33
  const { query, fetchOptions: argFetchOptions, ...body } = arg;
@@ -61,9 +61,10 @@ async function getTrustedOrigins(options, request) {
61
61
  const trustedOrigins = [];
62
62
  if (isDynamicBaseURLConfig(options.baseURL)) {
63
63
  const allowedHosts = options.baseURL.allowedHosts;
64
+ const proto = options.baseURL.protocol;
64
65
  for (const host of allowedHosts) if (!host.includes("://")) {
65
- trustedOrigins.push(`https://${host}`);
66
- if (isLoopbackHost(host)) trustedOrigins.push(`http://${host}`);
66
+ if (!proto || proto === "https" || proto === "auto") trustedOrigins.push(`https://${host}`);
67
+ if (proto === "http" || proto === "auto" || isLoopbackHost(host)) trustedOrigins.push(`http://${host}`);
67
68
  } else trustedOrigins.push(host);
68
69
  if (options.baseURL.fallback) try {
69
70
  trustedOrigins.push(new URL(options.baseURL.fallback).origin);
@@ -33,6 +33,20 @@ declare function stripSecureCookiePrefix(cookieName: string): string;
33
33
  declare function splitSetCookieHeader(setCookie: string): string[];
34
34
  declare function parseSetCookieHeader(setCookie: string): Map<string, CookieAttributes>;
35
35
  declare function toCookieOptions(attributes: CookieAttributes): ParsedCookieOptions;
36
+ /**
37
+ * Cookie-name token char set per RFC 7230 §3.2.6.
38
+ *
39
+ * @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
40
+ */
41
+ declare const cookieNameRegex: RegExp;
42
+ /**
43
+ * Tolerates `;` separators without the SP that RFC 6265 §4.2.1 mandates,
44
+ * since proxies and runtimes commonly strip it. Silently drops entries
45
+ * whose name violates RFC 7230 token or whose value violates RFC 6265
46
+ * cookie-octet (plus space and comma). Strips optional surrounding
47
+ * double-quotes per RFC 6265 §4.1.1.
48
+ */
49
+ declare function parseCookies(cookie: string): Map<string, string>;
36
50
  /**
37
51
  * Add or replace a cookie in the request `Cookie` header.
38
52
  *
@@ -42,8 +56,17 @@ declare function toCookieOptions(attributes: CookieAttributes): ParsedCookieOpti
42
56
  * parse-mutate-serialize.
43
57
  */
44
58
  declare function setRequestCookie(headers: Headers, name: string, value: string): void;
59
+ /**
60
+ * Merge `Set-Cookie` header values into the target's `Cookie` header.
61
+ * Mutates `target`.
62
+ *
63
+ * Name/value-level merge only. RFC 6265 §5 user-agent semantics
64
+ * (expiration, domain/path scoping, ordering) are out of scope. Suitable
65
+ * for single-request proxy, middleware, and test contexts.
66
+ */
67
+ declare function applySetCookies(target: Headers, setCookieValues: Iterable<string>): void;
45
68
  declare function setCookieToHeader(headers: Headers): (context: {
46
69
  response: Response;
47
70
  }) => void;
48
71
  //#endregion
49
- export { CookieAttributes, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, parseSetCookieHeader, setCookieToHeader, setRequestCookie, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions };
72
+ export { CookieAttributes, HOST_COOKIE_PREFIX, SECURE_COOKIE_PREFIX, applySetCookies, cookieNameRegex, parseCookies, parseSetCookieHeader, setCookieToHeader, setRequestCookie, splitSetCookieHeader, stripSecureCookiePrefix, toCookieOptions };