@better-auth/core 1.7.0-rc.4 → 1.7.0-rc.5

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.
@@ -6,7 +6,7 @@ import { AuthContext } from "../types/context.mjs";
6
6
  import "../types/index.mjs";
7
7
  import { OAuthProvider } from "../oauth2/oauth-provider.mjs";
8
8
  import "../oauth2/index.mjs";
9
- import { EndpointContext, EndpointOptions, StrictEndpoint } from "better-call";
9
+ import { EndpointHandler, EndpointOptions, MiddlewareHandler, StrictEndpoint } from "better-call";
10
10
  //#region src/api/index.d.ts
11
11
  /**
12
12
  * Response headers that forbid any intermediary (proxy, CDN, browser) from
@@ -26,12 +26,9 @@ declare const NO_STORE_HEADERS: {
26
26
  readonly "Cache-Control": "no-store";
27
27
  readonly Pragma: "no-cache";
28
28
  };
29
- declare const optionsMiddleware: <InputCtx extends import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>>(inputContext: InputCtx) => Promise<AuthContext>;
29
+ declare const optionsMiddleware: import("better-call").Middleware<import("better-call").MiddlewareOptions, <InputCtx extends import("better-call").MiddlewareInputContext<import("better-call").MiddlewareOptions>>(inputContext: InputCtx) => Promise<AuthContext>>;
30
30
  declare const createAuthMiddleware: {
31
- <Options extends import("better-call").MiddlewareOptions, R>(options: Options, handler: (ctx: import("better-call").MiddlewareContext<Options, {
32
- returned?: unknown | undefined;
33
- responseHeaders?: Headers | undefined;
34
- } & import("@better-auth/core").PluginContext<import("@better-auth/core").BetterAuthOptions> & import("@better-auth/core").InfoContext & {
31
+ <Options extends import("better-call").MiddlewareOptions, R>(options: Options, handler: (ctx: import("better-call").MiddlewareContext<Options, import("@better-auth/core").PluginContext<import("@better-auth/core").BetterAuthOptions> & import("@better-auth/core").InfoContext & {
35
32
  options: import("@better-auth/core").BetterAuthOptions;
36
33
  trustedOrigins: string[];
37
34
  trustedProviders: string[];
@@ -157,11 +154,11 @@ declare const createAuthMiddleware: {
157
154
  skipCSRFCheck: boolean;
158
155
  runInBackground: (promise: Promise<unknown>) => void;
159
156
  runInBackgroundOrAwait: (promise: Promise<unknown> | void) => import("@better-auth/core").Awaitable<unknown>;
160
- }>) => Promise<R>): (inputContext: import("better-call").MiddlewareInputContext<Options>) => Promise<R>;
161
- <Options extends import("better-call").MiddlewareOptions, R_1>(handler: (ctx: import("better-call").MiddlewareContext<Options, {
157
+ } & {
162
158
  returned?: unknown | undefined;
163
159
  responseHeaders?: Headers | undefined;
164
- } & import("@better-auth/core").PluginContext<import("@better-auth/core").BetterAuthOptions> & import("@better-auth/core").InfoContext & {
160
+ }>) => Promise<R>): import("better-call").Middleware<Options, (inputContext: import("better-call").MiddlewareInputContext<Options>) => Promise<R>>;
161
+ <Options extends import("better-call").MiddlewareOptions, R_1>(handler: (ctx: import("better-call").MiddlewareContext<Options, import("@better-auth/core").PluginContext<import("@better-auth/core").BetterAuthOptions> & import("@better-auth/core").InfoContext & {
165
162
  options: import("@better-auth/core").BetterAuthOptions;
166
163
  trustedOrigins: string[];
167
164
  trustedProviders: string[];
@@ -287,37 +284,19 @@ declare const createAuthMiddleware: {
287
284
  skipCSRFCheck: boolean;
288
285
  runInBackground: (promise: Promise<unknown>) => void;
289
286
  runInBackgroundOrAwait: (promise: Promise<unknown> | void) => import("@better-auth/core").Awaitable<unknown>;
290
- }>) => Promise<R_1>): (inputContext: import("better-call").MiddlewareInputContext<Options>) => Promise<R_1>;
287
+ } & {
288
+ returned?: unknown | undefined;
289
+ responseHeaders?: Headers | undefined;
290
+ }>) => Promise<R_1>): import("better-call").Middleware<Options, (inputContext: import("better-call").MiddlewareInputContext<Options>) => Promise<R_1>>;
291
291
  };
292
- type EndpointHandler<Path extends string, Options extends EndpointOptions, R> = (context: EndpointContext<Path, Options, AuthContext>) => Promise<R>;
293
- declare function createAuthEndpoint<Path extends string, Options extends EndpointOptions, R>(path: Path, options: Options, handler: EndpointHandler<Path, Options, R>): StrictEndpoint<Path, Options, R>;
294
- declare function createAuthEndpoint<Path extends string, Options extends EndpointOptions, R>(options: Options, handler: EndpointHandler<Path, Options, R>): StrictEndpoint<Path, Options, R>;
292
+ type AuthEndpointHandler<Path extends string, Options extends EndpointOptions, R> = EndpointHandler<Path, Options, R, AuthContext>;
293
+ type PathlessAuthEndpointHandler<Options extends EndpointOptions, R> = AuthEndpointHandler<string, Options, R>;
294
+ declare function createAuthEndpoint<Path extends string, Options extends EndpointOptions, R>(path: Path, options: Options, handler: AuthEndpointHandler<Path, Options, R>): StrictEndpoint<Path, Options, R>;
295
+ declare function createAuthEndpoint<InferredPath extends string, Options extends EndpointOptions, R>(options: Options, handler: PathlessAuthEndpointHandler<Options, R>): StrictEndpoint<InferredPath, Options, R>;
295
296
  declare namespace createAuthEndpoint {
296
- /**
297
- * Declare a **server-only** endpoint.
298
- *
299
- * The endpoint is callable through `auth.api.*` from trusted server code but is
300
- * never registered on the HTTP router and never emitted into the OpenAPI
301
- * schema. It takes no path because it has no URL to be reached at.
302
- *
303
- * Prefer this over the path-less `createAuthEndpoint({ ... }, handler)` form.
304
- * Setting `metadata.SERVER_ONLY` makes the intent explicit at the call site and
305
- * keeps the endpoint off the HTTP surface even if a path is later added by
306
- * mistake: better-call's router skips an endpoint when its path is missing *or*
307
- * when `SERVER_ONLY` is set, so the two together are defense in depth. Relying
308
- * on path omission alone is invisible and one keystroke away from exposure.
309
- *
310
- * @example
311
- * ```ts
312
- * viewBackupCodes: createAuthEndpoint.serverOnly(
313
- * { method: "POST", body: schema },
314
- * async (ctx) => { ... },
315
- * )
316
- * ```
317
- */
318
- function serverOnly<Path extends string, Options extends EndpointOptions, R>(options: Options, handler: EndpointHandler<Path, Options, R>): StrictEndpoint<Path, Options, R>;
297
+ var serverOnly: <InferredPath extends string, Options extends EndpointOptions, R>(options: Options, handler: PathlessAuthEndpointHandler<Options, R>) => StrictEndpoint<InferredPath, Options, R>;
319
298
  }
320
299
  type AuthEndpoint<Path extends string, Opts extends EndpointOptions, R> = ReturnType<typeof createAuthEndpoint<Path, Opts, R>>;
321
- type AuthMiddleware = ReturnType<typeof createAuthMiddleware>;
300
+ type AuthMiddleware = MiddlewareHandler;
322
301
  //#endregion
323
302
  export { AuthEndpoint, AuthMiddleware, NO_STORE_HEADERS, createAuthEndpoint, createAuthMiddleware, optionsMiddleware };
@@ -47,30 +47,26 @@ const optionsMiddleware = createMiddleware(async () => {
47
47
  const createAuthMiddleware = createMiddleware.create({ use: [optionsMiddleware, createMiddleware(async () => {
48
48
  return {};
49
49
  })] });
50
- const use = [optionsMiddleware];
51
- function createAuthEndpoint(pathOrOptions, handlerOrOptions, handlerOrNever) {
52
- const path = typeof pathOrOptions === "string" ? pathOrOptions : void 0;
53
- const options = typeof handlerOrOptions === "object" ? handlerOrOptions : pathOrOptions;
54
- const handler = typeof handlerOrOptions === "function" ? handlerOrOptions : handlerOrNever;
50
+ const createEndpointWithAuthContext = createEndpoint.create({ use: [optionsMiddleware] });
51
+ function wrapEndpointHandler(handler, options) {
55
52
  const noStore = options.metadata?.noStore === true;
56
- const wrapped = async (ctx) => {
57
- if (noStore) for (const [name, value] of Object.entries(NO_STORE_HEADERS)) ctx.setHeader(name, value);
58
- const runtimeCtx = ctx;
53
+ return async (context) => {
54
+ if (noStore) for (const [name, value] of Object.entries(NO_STORE_HEADERS)) context.setHeader(name, value);
59
55
  try {
60
- return await runWithEndpointContext(ctx, () => handler(ctx));
61
- } catch (e) {
62
- attachResponseHeadersToAPIError(runtimeCtx.responseHeaders, e);
63
- throw e;
56
+ return await runWithEndpointContext(context, () => handler(context));
57
+ } catch (error) {
58
+ attachResponseHeadersToAPIError(context.responseHeaders, error);
59
+ throw error;
64
60
  }
65
61
  };
66
- if (path) return createEndpoint(path, {
67
- ...options,
68
- use: [...options?.use || [], ...use]
69
- }, wrapped);
70
- return createEndpoint({
71
- ...options,
72
- use: [...options?.use || [], ...use]
73
- }, wrapped);
62
+ }
63
+ function createAuthEndpoint(...args) {
64
+ if (args.length === 3) {
65
+ const [path, options, handler] = args;
66
+ return createEndpointWithAuthContext(path, options, wrapEndpointHandler(handler, options));
67
+ }
68
+ const [options, handler] = args;
69
+ return createEndpointWithAuthContext(options, wrapEndpointHandler(handler, options));
74
70
  }
75
71
  /**
76
72
  * Set `metadata.SERVER_ONLY` while preserving any existing metadata
@@ -85,11 +81,28 @@ function withServerOnly(options) {
85
81
  }
86
82
  };
87
83
  }
88
- (function(_createAuthEndpoint) {
89
- function serverOnly(options, handler) {
90
- return createAuthEndpoint(withServerOnly(options), handler);
91
- }
92
- _createAuthEndpoint.serverOnly = serverOnly;
93
- })(createAuthEndpoint || (createAuthEndpoint = {}));
84
+ /**
85
+ * Declare a **server-only** endpoint.
86
+ *
87
+ * The endpoint is callable through `auth.api.*` from trusted server code but is
88
+ * never registered on the HTTP router and never emitted into the OpenAPI
89
+ * schema. It takes no path because it has no URL to be reached at.
90
+ *
91
+ * Prefer this over the path-less `createAuthEndpoint({ ... }, handler)` form.
92
+ * Setting `metadata.SERVER_ONLY` makes the intent explicit at the call site and
93
+ * keeps the endpoint off the HTTP surface even if a path is later added by
94
+ * mistake: better-call's router skips an endpoint when its path is missing *or*
95
+ * when `SERVER_ONLY` is set, so the two together are defense in depth. Relying
96
+ * on path omission alone is invisible and one keystroke away from exposure.
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * viewBackupCodes: createAuthEndpoint.serverOnly(
101
+ * { method: "POST", body: schema },
102
+ * async (ctx) => { ... },
103
+ * )
104
+ * ```
105
+ */
106
+ createAuthEndpoint.serverOnly = (options, handler) => createAuthEndpoint(withServerOnly(options), handler);
94
107
  //#endregion
95
108
  export { NO_STORE_HEADERS, createAuthEndpoint, createAuthMiddleware, optionsMiddleware };
@@ -2,7 +2,7 @@
2
2
  const symbol = Symbol.for("better-auth:global");
3
3
  let bind = null;
4
4
  const __context = {};
5
- const __betterAuthVersion = "1.7.0-rc.4";
5
+ const __betterAuthVersion = "1.7.0-rc.5";
6
6
  /**
7
7
  * We store context instance in the globalThis.
8
8
  *
@@ -2,7 +2,7 @@ import { ATTR_HTTP_RESPONSE_STATUS_CODE } from "./attributes.mjs";
2
2
  import { getOpenTelemetryAPI } from "./api.mjs";
3
3
  //#region src/instrumentation/tracer.ts
4
4
  const INSTRUMENTATION_SCOPE = "better-auth";
5
- const INSTRUMENTATION_VERSION = "1.7.0-rc.4";
5
+ const INSTRUMENTATION_VERSION = "1.7.0-rc.5";
6
6
  /**
7
7
  * Better-auth uses `throw ctx.redirect(url)` for flow control (e.g. OAuth
8
8
  * callbacks). These are APIErrors with 3xx status codes and should not be
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-auth/core",
3
- "version": "1.7.0-rc.4",
3
+ "version": "1.7.0-rc.5",
4
4
  "description": "The most comprehensive authentication framework for TypeScript.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -166,7 +166,7 @@
166
166
  "@opentelemetry/api": "^1.9.1",
167
167
  "@opentelemetry/sdk-trace-base": "^2.10.0",
168
168
  "@opentelemetry/sdk-trace-node": "^2.10.0",
169
- "better-call": "1.3.7",
169
+ "better-call": "1.4.0",
170
170
  "jose": "^6.2.3",
171
171
  "kysely": "^0.28.17 || ^0.29.0",
172
172
  "nanostores": "^1.3.0",
@@ -177,7 +177,7 @@
177
177
  "@better-fetch/fetch": "1.3.1",
178
178
  "@cloudflare/workers-types": ">=4",
179
179
  "@opentelemetry/api": "^1.9.0",
180
- "better-call": "1.3.7",
180
+ "better-call": "1.4.0",
181
181
  "jose": "^6.1.0",
182
182
  "kysely": "^0.28.5 || ^0.29.0",
183
183
  "nanostores": "^1.0.1"
package/src/api/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type {
2
- EndpointContext,
2
+ EndpointHandler,
3
3
  EndpointOptions,
4
+ MiddlewareHandler,
4
5
  StrictEndpoint,
5
6
  } from "better-call";
6
7
  import {
@@ -75,94 +76,93 @@ export const createAuthMiddleware = createMiddleware.create({
75
76
  ],
76
77
  });
77
78
 
78
- const use = [optionsMiddleware];
79
+ const createEndpointWithAuthContext = createEndpoint.create({
80
+ use: [optionsMiddleware],
81
+ });
79
82
 
80
- type EndpointHandler<
83
+ type AuthEndpointHandler<
81
84
  Path extends string,
82
85
  Options extends EndpointOptions,
83
86
  R,
84
- > = (context: EndpointContext<Path, Options, AuthContext>) => Promise<R>;
87
+ > = EndpointHandler<Path, Options, R, AuthContext>;
85
88
 
86
- export function createAuthEndpoint<
87
- Path extends string,
89
+ type PathlessAuthEndpointHandler<
88
90
  Options extends EndpointOptions,
89
91
  R,
90
- >(
91
- path: Path,
92
- options: Options,
93
- handler: EndpointHandler<Path, Options, R>,
94
- ): StrictEndpoint<Path, Options, R>;
92
+ > = AuthEndpointHandler<string, Options, R>;
95
93
 
96
- export function createAuthEndpoint<
94
+ function wrapEndpointHandler<
97
95
  Path extends string,
98
96
  Options extends EndpointOptions,
99
97
  R,
100
98
  >(
99
+ handler: AuthEndpointHandler<Path, Options, R>,
101
100
  options: Options,
102
- handler: EndpointHandler<Path, Options, R>,
103
- ): StrictEndpoint<Path, Options, R>;
104
-
105
- export function createAuthEndpoint<
106
- Path extends string,
107
- Opts extends EndpointOptions,
108
- R,
109
- >(
110
- pathOrOptions: Path | Opts,
111
- handlerOrOptions: EndpointHandler<Path, Opts, R> | Opts,
112
- handlerOrNever?: any,
113
- ) {
114
- const path: Path | undefined =
115
- typeof pathOrOptions === "string" ? pathOrOptions : undefined;
116
- const options: Opts =
117
- typeof handlerOrOptions === "object"
118
- ? handlerOrOptions
119
- : (pathOrOptions as Opts);
120
- const handler: EndpointHandler<Path, Opts, R> =
121
- typeof handlerOrOptions === "function" ? handlerOrOptions : handlerOrNever;
122
-
123
- // Endpoints that return credentials declare `metadata: { noStore: true }`.
124
- // Emit the no-store headers at the boundary, before the handler runs, so they
125
- // land on every response the handler produces: a success harvests
126
- // `responseHeaders`, and a thrown error carries the same headers through
127
- // `attachResponseHeadersToAPIError`. Validation that rejects the request
128
- // before the handler runs is not covered (and returns no credentials).
101
+ ): AuthEndpointHandler<Path, Options, R> {
129
102
  const noStore =
130
103
  (options as { metadata?: { noStore?: boolean } }).metadata?.noStore ===
131
104
  true;
132
105
 
133
- // todo: prettify the code, we want to call `runWithEndpointContext` to top level
134
- const wrapped: EndpointHandler<Path, Opts, R> = async (ctx) => {
106
+ return async (context) => {
135
107
  if (noStore) {
136
108
  for (const [name, value] of Object.entries(NO_STORE_HEADERS)) {
137
- ctx.setHeader(name, value);
109
+ context.setHeader(name, value);
138
110
  }
139
111
  }
140
- const runtimeCtx = ctx as unknown as { responseHeaders?: Headers };
141
112
  try {
142
- return await runWithEndpointContext(ctx as any, () => handler(ctx));
143
- } catch (e) {
144
- attachResponseHeadersToAPIError(runtimeCtx.responseHeaders, e);
145
- throw e;
113
+ return await runWithEndpointContext(context, () => handler(context));
114
+ } catch (error) {
115
+ attachResponseHeadersToAPIError(context.responseHeaders, error);
116
+ throw error;
146
117
  }
147
118
  };
119
+ }
148
120
 
149
- if (path) {
150
- return createEndpoint(
121
+ export function createAuthEndpoint<
122
+ Path extends string,
123
+ Options extends EndpointOptions,
124
+ R,
125
+ >(
126
+ path: Path,
127
+ options: Options,
128
+ handler: AuthEndpointHandler<Path, Options, R>,
129
+ ): StrictEndpoint<Path, Options, R>;
130
+
131
+ export function createAuthEndpoint<
132
+ InferredPath extends string,
133
+ Options extends EndpointOptions,
134
+ R,
135
+ >(
136
+ options: Options,
137
+ handler: PathlessAuthEndpointHandler<Options, R>,
138
+ ): StrictEndpoint<InferredPath, Options, R>;
139
+
140
+ export function createAuthEndpoint<
141
+ Path extends string,
142
+ Options extends EndpointOptions,
143
+ R,
144
+ >(
145
+ ...args:
146
+ | [
147
+ path: Path,
148
+ options: Options,
149
+ handler: AuthEndpointHandler<Path, Options, R>,
150
+ ]
151
+ | [options: Options, handler: PathlessAuthEndpointHandler<Options, R>]
152
+ ) {
153
+ if (args.length === 3) {
154
+ const [path, options, handler] = args;
155
+ return createEndpointWithAuthContext(
151
156
  path,
152
- {
153
- ...options,
154
- use: [...(options?.use || []), ...use],
155
- },
156
- wrapped,
157
+ options,
158
+ wrapEndpointHandler(handler, options),
157
159
  );
158
160
  }
159
161
 
160
- return createEndpoint(
161
- {
162
- ...options,
163
- use: [...(options?.use || []), ...use],
164
- },
165
- wrapped,
162
+ const [options, handler] = args;
163
+ return createEndpointWithAuthContext(
164
+ options,
165
+ wrapEndpointHandler(handler, options),
166
166
  );
167
167
  }
168
168
 
@@ -179,44 +179,45 @@ function withServerOnly<Options extends EndpointOptions>(
179
179
  } as Options;
180
180
  }
181
181
 
182
- export namespace createAuthEndpoint {
183
- /**
184
- * Declare a **server-only** endpoint.
185
- *
186
- * The endpoint is callable through `auth.api.*` from trusted server code but is
187
- * never registered on the HTTP router and never emitted into the OpenAPI
188
- * schema. It takes no path because it has no URL to be reached at.
189
- *
190
- * Prefer this over the path-less `createAuthEndpoint({ ... }, handler)` form.
191
- * Setting `metadata.SERVER_ONLY` makes the intent explicit at the call site and
192
- * keeps the endpoint off the HTTP surface even if a path is later added by
193
- * mistake: better-call's router skips an endpoint when its path is missing *or*
194
- * when `SERVER_ONLY` is set, so the two together are defense in depth. Relying
195
- * on path omission alone is invisible and one keystroke away from exposure.
196
- *
197
- * @example
198
- * ```ts
199
- * viewBackupCodes: createAuthEndpoint.serverOnly(
200
- * { method: "POST", body: schema },
201
- * async (ctx) => { ... },
202
- * )
203
- * ```
204
- */
205
- export function serverOnly<
206
- Path extends string,
207
- Options extends EndpointOptions,
208
- R,
209
- >(
210
- options: Options,
211
- handler: EndpointHandler<Path, Options, R>,
212
- ): StrictEndpoint<Path, Options, R> {
213
- return createAuthEndpoint(withServerOnly(options), handler);
214
- }
215
- }
182
+ /**
183
+ * Declare a **server-only** endpoint.
184
+ *
185
+ * The endpoint is callable through `auth.api.*` from trusted server code but is
186
+ * never registered on the HTTP router and never emitted into the OpenAPI
187
+ * schema. It takes no path because it has no URL to be reached at.
188
+ *
189
+ * Prefer this over the path-less `createAuthEndpoint({ ... }, handler)` form.
190
+ * Setting `metadata.SERVER_ONLY` makes the intent explicit at the call site and
191
+ * keeps the endpoint off the HTTP surface even if a path is later added by
192
+ * mistake: better-call's router skips an endpoint when its path is missing *or*
193
+ * when `SERVER_ONLY` is set, so the two together are defense in depth. Relying
194
+ * on path omission alone is invisible and one keystroke away from exposure.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * viewBackupCodes: createAuthEndpoint.serverOnly(
199
+ * { method: "POST", body: schema },
200
+ * async (ctx) => { ... },
201
+ * )
202
+ * ```
203
+ */
204
+ createAuthEndpoint.serverOnly = <
205
+ InferredPath extends string,
206
+ Options extends EndpointOptions,
207
+ R,
208
+ >(
209
+ options: Options,
210
+ handler: PathlessAuthEndpointHandler<Options, R>,
211
+ ): StrictEndpoint<InferredPath, Options, R> =>
212
+ createAuthEndpoint<InferredPath, Options, R>(
213
+ withServerOnly(options),
214
+ handler,
215
+ );
216
216
 
217
217
  export type AuthEndpoint<
218
218
  Path extends string,
219
219
  Opts extends EndpointOptions,
220
220
  R,
221
221
  > = ReturnType<typeof createAuthEndpoint<Path, Opts, R>>;
222
- export type AuthMiddleware = ReturnType<typeof createAuthMiddleware>;
222
+
223
+ export type AuthMiddleware = MiddlewareHandler;