refactor: move client plugin types to core (#5184)

This commit is contained in:
Alex Yang
2025-10-09 00:51:32 -07:00
committed by GitHub
parent ba86dadf82
commit 9a1bcb3339
40 changed files with 310 additions and 258 deletions

View File

@@ -1,12 +1,18 @@
import { createFetch } from "@better-fetch/fetch"; import { createFetch } from "@better-fetch/fetch";
import { getBaseURL } from "../utils/url"; import { getBaseURL } from "../utils/url";
import { type WritableAtom } from "nanostores"; import { type WritableAtom } from "nanostores";
import type { AtomListener, ClientOptions } from "./types"; import type {
BetterAuthClientOptions,
ClientAtomListener,
} from "@better-auth/core";
import { redirectPlugin } from "./fetch-plugins"; import { redirectPlugin } from "./fetch-plugins";
import { getSessionAtom } from "./session-atom"; import { getSessionAtom } from "./session-atom";
import { parseJSON } from "./parser"; import { parseJSON } from "./parser";
export const getClientConfig = (options?: ClientOptions, loadEnv?: boolean) => { export const getClientConfig = (
options?: BetterAuthClientOptions,
loadEnv?: boolean,
) => {
/* check if the credentials property is supported. Useful for cf workers */ /* check if the credentials property is supported. Useful for cf workers */
const isCredentialsSupported = "credentials" in Request.prototype; const isCredentialsSupported = "credentials" in Request.prototype;
const baseURL = const baseURL =
@@ -62,7 +68,7 @@ export const getClientConfig = (options?: ClientOptions, loadEnv?: boolean) => {
"/revoke-other-sessions": "POST", "/revoke-other-sessions": "POST",
"/delete-user": "POST", "/delete-user": "POST",
}; };
const atomListeners: AtomListener[] = [ const atomListeners: ClientAtomListener[] = [
{ {
signal: "$sessionSignal", signal: "$sessionSignal",
matcher(path) { matcher(path) {

View File

@@ -1,5 +1,5 @@
import type { BetterAuthOptions, BetterAuthPlugin } from "@better-auth/core"; import type { BetterAuthOptions, BetterAuthPlugin } from "@better-auth/core";
import type { BetterAuthClientPlugin } from "./types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export * from "./vanilla"; export * from "./vanilla";
export * from "./query"; export * from "./query";
export * from "./types"; export * from "./types";
@@ -15,6 +15,6 @@ export function InferAuth<O extends { options: BetterAuthOptions }>() {
return {} as O["options"]; return {} as O["options"];
} }
//@ts-expect-error // @ts-expect-error
export type * from "nanostores"; export type * from "nanostores";
export type * from "@better-fetch/fetch"; export type * from "@better-fetch/fetch";

View File

@@ -1,12 +1,14 @@
import { getClientConfig } from "../config"; import { getClientConfig } from "../config";
import type { import type {
BetterAuthClientPlugin,
ClientOptions,
InferActions, InferActions,
InferClientAPI, InferClientAPI,
InferErrorCodes, InferErrorCodes,
IsSignal, IsSignal,
} from "../types"; } from "../types";
import type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
} from "@better-auth/core";
import { createDynamicPathProxy } from "../proxy"; import { createDynamicPathProxy } from "../proxy";
import type { PrettifyDeep, UnionToIntersection } from "../../types/helper"; import type { PrettifyDeep, UnionToIntersection } from "../../types/helper";
import type { import type {
@@ -25,25 +27,24 @@ export function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1); return str.charAt(0).toUpperCase() + str.slice(1);
} }
type InferResolvedHooks<O extends ClientOptions> = O["plugins"] extends Array< type InferResolvedHooks<O extends BetterAuthClientOptions> =
infer Plugin O["plugins"] extends Array<infer Plugin>
> ? Plugin extends BetterAuthClientPlugin
? Plugin extends BetterAuthClientPlugin ? Plugin["getAtoms"] extends (fetch: any) => infer Atoms
? Plugin["getAtoms"] extends (fetch: any) => infer Atoms ? Atoms extends Record<string, any>
? Atoms extends Record<string, any> ? {
? { [key in keyof Atoms as IsSignal<key> extends true
[key in keyof Atoms as IsSignal<key> extends true ? never
? never : key extends string
: key extends string ? `use${Capitalize<key>}`
? `use${Capitalize<key>}` : never]: () => ReturnType<Atoms[key]["get"]>;
: never]: () => ReturnType<Atoms[key]["get"]>; }
} : {}
: {} : {}
: {} : {}
: {} : {};
: {};
export function createAuthClient<Option extends ClientOptions>( export function createAuthClient<Option extends BetterAuthClientOptions>(
options?: Option, options?: Option,
) { ) {
const { const {

View File

@@ -9,11 +9,11 @@ import type {
UnionToIntersection, UnionToIntersection,
} from "../types/helper"; } from "../types/helper";
import type { import type {
ClientOptions,
InferAdditionalFromClient, InferAdditionalFromClient,
InferSessionFromClient, InferSessionFromClient,
InferUserFromClient, InferUserFromClient,
} from "./types"; } from "./types";
import type { BetterAuthClientOptions } from "@better-auth/core";
export type CamelCase<S extends string> = export type CamelCase<S extends string> =
S extends `${infer P1}-${infer P2}${infer P3}` S extends `${infer P1}-${infer P2}${infer P3}`
@@ -30,7 +30,7 @@ export type PathToObject<
: never; : never;
export type InferSignUpEmailCtx< export type InferSignUpEmailCtx<
ClientOpts extends ClientOptions, ClientOpts extends BetterAuthClientOptions,
FetchOptions extends BetterFetchOption, FetchOptions extends BetterFetchOption,
> = { > = {
email: string; email: string;
@@ -42,7 +42,7 @@ export type InferSignUpEmailCtx<
} & UnionToIntersection<InferAdditionalFromClient<ClientOpts, "user", "input">>; } & UnionToIntersection<InferAdditionalFromClient<ClientOpts, "user", "input">>;
export type InferUserUpdateCtx< export type InferUserUpdateCtx<
ClientOpts extends ClientOptions, ClientOpts extends BetterAuthClientOptions,
FetchOptions extends BetterFetchOption, FetchOptions extends BetterFetchOption,
> = { > = {
image?: string | null; image?: string | null;
@@ -75,10 +75,10 @@ export type InferCtx<
export type MergeRoutes<T> = UnionToIntersection<T>; export type MergeRoutes<T> = UnionToIntersection<T>;
export type InferRoute<API, COpts extends ClientOptions> = API extends Record< export type InferRoute<
string, API,
infer T COpts extends BetterAuthClientOptions,
> > = API extends Record<string, infer T>
? T extends Endpoint ? T extends Endpoint
? T["options"]["metadata"] extends ? T["options"]["metadata"] extends
| { | {
@@ -154,7 +154,7 @@ export type InferRoute<API, COpts extends ClientOptions> = API extends Record<
export type InferRoutes< export type InferRoutes<
API extends Record<string, Endpoint>, API extends Record<string, Endpoint>,
ClientOpts extends ClientOptions, ClientOpts extends BetterAuthClientOptions,
> = MergeRoutes<InferRoute<API, ClientOpts>>; > = MergeRoutes<InferRoute<API, ClientOpts>>;
export type ProxyRequest = { export type ProxyRequest = {

View File

@@ -1,4 +1,4 @@
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
import type { BetterAuthOptions } from "@better-auth/core"; import type { BetterAuthOptions } from "@better-auth/core";
export const InferServerPlugin = < export const InferServerPlugin = <

View File

@@ -1,7 +1,7 @@
import type { BetterFetch, BetterFetchOption } from "@better-fetch/fetch"; import type { BetterFetch, BetterFetchOption } from "@better-fetch/fetch";
import type { Atom, PreinitializedWritableAtom } from "nanostores"; import type { Atom, PreinitializedWritableAtom } from "nanostores";
import type { ProxyRequest } from "./path-to-object"; import type { ProxyRequest } from "./path-to-object";
import type { BetterAuthClientPlugin } from "./types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
import { isAtom } from "../utils/is-atom"; import { isAtom } from "../utils/is-atom";
function getMethod( function getMethod(

View File

@@ -1,12 +1,14 @@
import { getClientConfig } from "../config"; import { getClientConfig } from "../config";
import type { import type {
BetterAuthClientPlugin,
ClientOptions,
InferActions, InferActions,
InferClientAPI, InferClientAPI,
InferErrorCodes, InferErrorCodes,
IsSignal, IsSignal,
} from "../types"; } from "../types";
import type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
} from "@better-auth/core";
import { createDynamicPathProxy } from "../proxy"; import { createDynamicPathProxy } from "../proxy";
import type { PrettifyDeep, UnionToIntersection } from "../../types/helper"; import type { PrettifyDeep, UnionToIntersection } from "../../types/helper";
import type { import type {
@@ -25,25 +27,24 @@ export function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1); return str.charAt(0).toUpperCase() + str.slice(1);
} }
type InferResolvedHooks<O extends ClientOptions> = O["plugins"] extends Array< type InferResolvedHooks<O extends BetterAuthClientOptions> =
infer Plugin O["plugins"] extends Array<infer Plugin>
> ? Plugin extends BetterAuthClientPlugin
? Plugin extends BetterAuthClientPlugin ? Plugin["getAtoms"] extends (fetch: any) => infer Atoms
? Plugin["getAtoms"] extends (fetch: any) => infer Atoms ? Atoms extends Record<string, any>
? Atoms extends Record<string, any> ? {
? { [key in keyof Atoms as IsSignal<key> extends true
[key in keyof Atoms as IsSignal<key> extends true ? never
? never : key extends string
: key extends string ? `use${Capitalize<key>}`
? `use${Capitalize<key>}` : never]: () => ReturnType<Atoms[key]["get"]>;
: never]: () => ReturnType<Atoms[key]["get"]>; }
} : {}
: {} : {}
: {} : {}
: {} : {};
: {};
export function createAuthClient<Option extends ClientOptions>( export function createAuthClient<Option extends BetterAuthClientOptions>(
options?: Option, options?: Option,
) { ) {
const { const {

View File

@@ -2,13 +2,16 @@ import { getClientConfig } from "../config";
import { createDynamicPathProxy } from "../proxy"; import { createDynamicPathProxy } from "../proxy";
import { capitalizeFirstLetter } from "../../utils/misc"; import { capitalizeFirstLetter } from "../../utils/misc";
import type { import type {
BetterAuthClientPlugin,
ClientOptions,
InferActions, InferActions,
InferClientAPI, InferClientAPI,
InferErrorCodes, InferErrorCodes,
IsSignal, IsSignal,
} from "../types"; } from "../types";
import type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
} from "@better-auth/core";
import type { Accessor } from "solid-js"; import type { Accessor } from "solid-js";
import type { PrettifyDeep, UnionToIntersection } from "../../types/helper"; import type { PrettifyDeep, UnionToIntersection } from "../../types/helper";
import type { import type {
@@ -22,25 +25,24 @@ function getAtomKey(str: string) {
return `use${capitalizeFirstLetter(str)}`; return `use${capitalizeFirstLetter(str)}`;
} }
type InferResolvedHooks<O extends ClientOptions> = O["plugins"] extends Array< type InferResolvedHooks<O extends BetterAuthClientOptions> =
infer Plugin O["plugins"] extends Array<infer Plugin>
> ? Plugin extends BetterAuthClientPlugin
? Plugin extends BetterAuthClientPlugin ? Plugin["getAtoms"] extends (fetch: any) => infer Atoms
? Plugin["getAtoms"] extends (fetch: any) => infer Atoms ? Atoms extends Record<string, any>
? Atoms extends Record<string, any> ? {
? { [key in keyof Atoms as IsSignal<key> extends true
[key in keyof Atoms as IsSignal<key> extends true ? never
? never : key extends string
: key extends string ? `use${Capitalize<key>}`
? `use${Capitalize<key>}` : never]: () => Accessor<ReturnType<Atoms[key]["get"]>>;
: never]: () => Accessor<ReturnType<Atoms[key]["get"]>>; }
} : {}
: {} : {}
: {} : {}
: {} : {};
: {};
export function createAuthClient<Option extends ClientOptions>( export function createAuthClient<Option extends BetterAuthClientOptions>(
options?: Option, options?: Option,
) { ) {
const { const {

View File

@@ -1,13 +1,15 @@
import { getClientConfig } from "../config"; import { getClientConfig } from "../config";
import { capitalizeFirstLetter } from "../../utils/misc"; import { capitalizeFirstLetter } from "../../utils/misc";
import type { import type {
BetterAuthClientPlugin,
ClientOptions,
InferActions, InferActions,
InferClientAPI, InferClientAPI,
InferErrorCodes, InferErrorCodes,
IsSignal, IsSignal,
} from "../types"; } from "../types";
import type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
} from "@better-auth/core";
import { createDynamicPathProxy } from "../proxy"; import { createDynamicPathProxy } from "../proxy";
import type { PrettifyDeep, UnionToIntersection } from "../../types/helper"; import type { PrettifyDeep, UnionToIntersection } from "../../types/helper";
import type { Atom } from "nanostores"; import type { Atom } from "nanostores";
@@ -17,25 +19,24 @@ import type {
} from "@better-fetch/fetch"; } from "@better-fetch/fetch";
import type { BASE_ERROR_CODES } from "@better-auth/core/error"; import type { BASE_ERROR_CODES } from "@better-auth/core/error";
type InferResolvedHooks<O extends ClientOptions> = O["plugins"] extends Array< type InferResolvedHooks<O extends BetterAuthClientOptions> =
infer Plugin O["plugins"] extends Array<infer Plugin>
> ? Plugin extends BetterAuthClientPlugin
? Plugin extends BetterAuthClientPlugin ? Plugin["getAtoms"] extends (fetch: any) => infer Atoms
? Plugin["getAtoms"] extends (fetch: any) => infer Atoms ? Atoms extends Record<string, any>
? Atoms extends Record<string, any> ? {
? { [key in keyof Atoms as IsSignal<key> extends true
[key in keyof Atoms as IsSignal<key> extends true ? never
? never : key extends string
: key extends string ? `use${Capitalize<key>}`
? `use${Capitalize<key>}` : never]: () => Atoms[key];
: never]: () => Atoms[key]; }
} : {}
: {} : {}
: {} : {}
: {} : {};
: {};
export function createAuthClient<Option extends ClientOptions>( export function createAuthClient<Option extends BetterAuthClientOptions>(
options?: Option, options?: Option,
) { ) {
const { const {

View File

@@ -1,5 +1,5 @@
import { atom, computed } from "nanostores"; import { atom, computed } from "nanostores";
import type { BetterAuthClientPlugin } from "./types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
import type { BetterAuthPlugin } from "@better-auth/core"; import type { BetterAuthPlugin } from "@better-auth/core";
import { createAuthEndpoint } from "@better-auth/core/middleware"; import { createAuthEndpoint } from "@better-auth/core/middleware";
import { useAuthQuery } from "./query"; import { useAuthQuery } from "./query";

View File

@@ -1,81 +1,36 @@
import type { import type { BetterAuthPlugin } from "@better-auth/core";
BetterFetch, import type { StripEmptyObjects, UnionToIntersection } from "../types/helper";
BetterFetchOption,
BetterFetchPlugin,
} from "@better-fetch/fetch";
import type { BetterAuthPlugin, BetterAuthOptions } from "@better-auth/core";
import type { Atom, WritableAtom } from "nanostores";
import type {
LiteralString,
StripEmptyObjects,
UnionToIntersection,
} from "../types/helper";
import type { Auth } from "../auth"; import type { Auth } from "../auth";
import type { InferRoutes } from "./path-to-object"; import type { InferRoutes } from "./path-to-object";
import type { Session, User } from "../types"; import type { Session, User } from "../types";
import type { InferFieldsInputClient, InferFieldsOutput } from "../db"; import type { InferFieldsInputClient, InferFieldsOutput } from "../db";
import type {
export type AtomListener = { ClientStore,
matcher: (path: string) => boolean; ClientAtomListener,
signal: "$sessionSignal" | Omit<string, "$sessionSignal">; BetterAuthClientOptions,
BetterAuthClientPlugin,
} from "@better-auth/core";
export type {
ClientStore,
ClientAtomListener,
BetterAuthClientOptions,
BetterAuthClientPlugin,
}; };
export interface Store { /**
notify: (signal: string) => void; * @deprecated use type `BetterAuthClientOptions` instead.
listen: (signal: string, listener: () => void) => void; */
atoms: Record<string, WritableAtom<any>>; export type Store = ClientStore;
} /**
* @deprecated use type `ClientAtomListener` instead.
*/
export type AtomListener = ClientAtomListener;
/**
* @deprecated use type `BetterAuthClientPlugin` instead.
*/
export type ClientOptions = BetterAuthClientOptions;
export interface BetterAuthClientPlugin { export type InferClientAPI<O extends BetterAuthClientOptions> = InferRoutes<
id: LiteralString;
/**
* only used for type inference. don't pass the
* actual plugin
*/
$InferServerPlugin?: BetterAuthPlugin;
/**
* Custom actions
*/
getActions?: (
$fetch: BetterFetch,
$store: Store,
/**
* better-auth client options
*/
options: ClientOptions | undefined,
) => Record<string, any>;
/**
* State atoms that'll be resolved by each framework
* auth store.
*/
getAtoms?: ($fetch: BetterFetch) => Record<string, Atom<any>>;
/**
* specify path methods for server plugin inferred
* endpoints to force a specific method.
*/
pathMethods?: Record<string, "POST" | "GET">;
/**
* Better fetch plugins
*/
fetchPlugins?: BetterFetchPlugin[];
/**
* a list of recaller based on a matcher function.
* The signal name needs to match a signal in this
* plugin or any plugin the user might have added.
*/
atomListeners?: AtomListener[];
}
export interface ClientOptions {
fetchOptions?: BetterFetchOption;
plugins?: BetterAuthClientPlugin[];
baseURL?: string;
basePath?: string;
disableDefaultFetchPlugins?: boolean;
$InferAuth?: BetterAuthOptions;
}
export type InferClientAPI<O extends ClientOptions> = InferRoutes<
O["plugins"] extends Array<any> O["plugins"] extends Array<any>
? Auth["api"] & ? Auth["api"] &
(O["plugins"] extends Array<infer Pl> (O["plugins"] extends Array<infer Pl>
@@ -95,34 +50,33 @@ export type InferClientAPI<O extends ClientOptions> = InferRoutes<
O O
>; >;
export type InferActions<O extends ClientOptions> = (O["plugins"] extends Array< export type InferActions<O extends BetterAuthClientOptions> =
infer Plugin (O["plugins"] extends Array<infer Plugin>
> ? UnionToIntersection<
? UnionToIntersection< Plugin extends BetterAuthClientPlugin
Plugin extends BetterAuthClientPlugin ? Plugin["getActions"] extends (...args: any) => infer Actions
? Plugin["getActions"] extends (...args: any) => infer Actions ? Actions
? Actions : {}
: {} : {}
: {} >
> : {}) &
: {}) & //infer routes from auth config
//infer routes from auth config InferRoutes<
InferRoutes< O["$InferAuth"] extends {
O["$InferAuth"] extends { plugins: infer Plugins;
plugins: infer Plugins; }
} ? Plugins extends Array<infer Plugin>
? Plugins extends Array<infer Plugin> ? Plugin extends {
? Plugin extends { endpoints: infer Endpoints;
endpoints: infer Endpoints; }
} ? Endpoints
? Endpoints : {}
: {} : {}
: {} : {},
: {}, O
O >;
>;
export type InferErrorCodes<O extends ClientOptions> = export type InferErrorCodes<O extends BetterAuthClientOptions> =
O["plugins"] extends Array<infer Plugin> O["plugins"] extends Array<infer Plugin>
? UnionToIntersection< ? UnionToIntersection<
Plugin extends BetterAuthClientPlugin Plugin extends BetterAuthClientPlugin
@@ -138,21 +92,23 @@ export type InferErrorCodes<O extends ClientOptions> =
*/ */
export type IsSignal<T> = T extends `$${infer _}` ? true : false; export type IsSignal<T> = T extends `$${infer _}` ? true : false;
export type InferPluginsFromClient<O extends ClientOptions> = export type InferPluginsFromClient<O extends BetterAuthClientOptions> =
O["plugins"] extends Array<BetterAuthClientPlugin> O["plugins"] extends Array<BetterAuthClientPlugin>
? Array<O["plugins"][number]["$InferServerPlugin"]> ? Array<O["plugins"][number]["$InferServerPlugin"]>
: undefined; : undefined;
export type InferSessionFromClient<O extends ClientOptions> = StripEmptyObjects< export type InferSessionFromClient<O extends BetterAuthClientOptions> =
Session & StripEmptyObjects<
UnionToIntersection<InferAdditionalFromClient<O, "session", "output">> Session &
>; UnionToIntersection<InferAdditionalFromClient<O, "session", "output">>
export type InferUserFromClient<O extends ClientOptions> = StripEmptyObjects< >;
User & UnionToIntersection<InferAdditionalFromClient<O, "user", "output">> export type InferUserFromClient<O extends BetterAuthClientOptions> =
>; StripEmptyObjects<
User & UnionToIntersection<InferAdditionalFromClient<O, "user", "output">>
>;
export type InferAdditionalFromClient< export type InferAdditionalFromClient<
Options extends ClientOptions, Options extends BetterAuthClientOptions,
Key extends string, Key extends string,
Format extends "input" | "output" = "output", Format extends "input" | "output" = "output",
> = Options["plugins"] extends Array<infer T> > = Options["plugins"] extends Array<infer T>

View File

@@ -1,13 +1,15 @@
import { getClientConfig } from "./config"; import { getClientConfig } from "./config";
import { capitalizeFirstLetter } from "../utils/misc"; import { capitalizeFirstLetter } from "../utils/misc";
import type { import type {
BetterAuthClientPlugin,
ClientOptions,
InferActions, InferActions,
InferClientAPI, InferClientAPI,
InferErrorCodes, InferErrorCodes,
IsSignal, IsSignal,
} from "./types"; } from "./types";
import type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
} from "@better-auth/core";
import { createDynamicPathProxy } from "./proxy"; import { createDynamicPathProxy } from "./proxy";
import type { PrettifyDeep, UnionToIntersection } from "../types/helper"; import type { PrettifyDeep, UnionToIntersection } from "../types/helper";
import type { Atom } from "nanostores"; import type { Atom } from "nanostores";
@@ -17,25 +19,24 @@ import type {
} from "@better-fetch/fetch"; } from "@better-fetch/fetch";
import type { BASE_ERROR_CODES } from "@better-auth/core/error"; import type { BASE_ERROR_CODES } from "@better-auth/core/error";
type InferResolvedHooks<O extends ClientOptions> = O["plugins"] extends Array< type InferResolvedHooks<O extends BetterAuthClientOptions> =
infer Plugin O["plugins"] extends Array<infer Plugin>
> ? Plugin extends BetterAuthClientPlugin
? Plugin extends BetterAuthClientPlugin ? Plugin["getAtoms"] extends (fetch: any) => infer Atoms
? Plugin["getAtoms"] extends (fetch: any) => infer Atoms ? Atoms extends Record<string, any>
? Atoms extends Record<string, any> ? {
? { [key in keyof Atoms as IsSignal<key> extends true
[key in keyof Atoms as IsSignal<key> extends true ? never
? never : key extends string
: key extends string ? `use${Capitalize<key>}`
? `use${Capitalize<key>}` : never]: Atoms[key];
: never]: Atoms[key]; }
} : {}
: {} : {}
: {} : {}
: {} : {};
: {};
export function createAuthClient<Option extends ClientOptions>( export function createAuthClient<Option extends BetterAuthClientOptions>(
options?: Option, options?: Option,
) { ) {
const { const {

View File

@@ -3,13 +3,15 @@ import type { DeepReadonly, Ref } from "vue";
import { getClientConfig } from "../config"; import { getClientConfig } from "../config";
import { capitalizeFirstLetter } from "../../utils/misc"; import { capitalizeFirstLetter } from "../../utils/misc";
import type { import type {
BetterAuthClientPlugin,
ClientOptions,
InferActions, InferActions,
InferClientAPI, InferClientAPI,
InferErrorCodes, InferErrorCodes,
IsSignal, IsSignal,
} from "../types"; } from "../types";
import type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
} from "@better-auth/core";
import { createDynamicPathProxy } from "../proxy"; import { createDynamicPathProxy } from "../proxy";
import type { PrettifyDeep, UnionToIntersection } from "../../types/helper"; import type { PrettifyDeep, UnionToIntersection } from "../../types/helper";
import type { import type {
@@ -22,27 +24,26 @@ function getAtomKey(str: string) {
return `use${capitalizeFirstLetter(str)}`; return `use${capitalizeFirstLetter(str)}`;
} }
type InferResolvedHooks<O extends ClientOptions> = O["plugins"] extends Array< type InferResolvedHooks<O extends BetterAuthClientOptions> =
infer Plugin O["plugins"] extends Array<infer Plugin>
> ? Plugin extends BetterAuthClientPlugin
? Plugin extends BetterAuthClientPlugin ? Plugin["getAtoms"] extends (fetch: any) => infer Atoms
? Plugin["getAtoms"] extends (fetch: any) => infer Atoms ? Atoms extends Record<string, any>
? Atoms extends Record<string, any> ? {
? { [key in keyof Atoms as IsSignal<key> extends true
[key in keyof Atoms as IsSignal<key> extends true ? never
? never : key extends string
: key extends string ? `use${Capitalize<key>}`
? `use${Capitalize<key>}` : never]: () => DeepReadonly<
: never]: () => DeepReadonly< Ref<ReturnType<Atoms[key]["get"]>>
Ref<ReturnType<Atoms[key]["get"]>> >;
>; }
} : {}
: {} : {}
: {} : {}
: {} : {};
: {};
export function createAuthClient<Option extends ClientOptions>( export function createAuthClient<Option extends BetterAuthClientOptions>(
options?: Option, options?: Option,
) { ) {
const { const {

View File

@@ -1,5 +1,6 @@
import type { DBFieldAttribute } from "@better-auth/core/db"; import type { DBFieldAttribute } from "@better-auth/core/db";
import type { BetterAuthClientPlugin, BetterAuthOptions } from "../../types"; import type { BetterAuthOptions } from "../../types";
import type { BetterAuthClientPlugin } from "@better-auth/core";
import type { BetterAuthPlugin } from "../../types"; import type { BetterAuthPlugin } from "../../types";
export const inferAdditionalFields = < export const inferAdditionalFields = <

View File

@@ -1,4 +1,4 @@
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
import { type AccessControl, type Role } from "../access"; import { type AccessControl, type Role } from "../access";
import { adminAc, defaultStatements, userAc } from "./access"; import { adminAc, defaultStatements, userAc } from "./access";
import type { admin } from "./admin"; import type { admin } from "./admin";

View File

@@ -1,5 +1,5 @@
import type { anonymous } from "."; import type { anonymous } from ".";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const anonymousClient = () => { export const anonymousClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { apiKey } from "."; import type { apiKey } from ".";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const apiKeyClient = () => { export const apiKeyClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { deviceAuthorization } from "."; import type { deviceAuthorization } from ".";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const deviceAuthorizationClient = () => { export const deviceAuthorizationClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { emailOTP } from "."; import type { emailOTP } from ".";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const emailOTPClient = () => { export const emailOTPClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { genericOAuth } from "."; import type { genericOAuth } from ".";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const genericOAuthClient = () => { export const genericOAuthClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { jwt } from "./index"; import type { jwt } from "./index";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const jwtClient = () => { export const jwtClient = () => {
return { return {

View File

@@ -1,4 +1,4 @@
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
/** /**
* Configuration for the client-side last login method plugin * Configuration for the client-side last login method plugin

View File

@@ -1,5 +1,5 @@
import type { magicLink } from "."; import type { magicLink } from ".";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const magicLinkClient = () => { export const magicLinkClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { multiSession } from "."; import type { multiSession } from ".";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const multiSessionClient = () => { export const multiSessionClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { oidcProvider } from "."; import type { oidcProvider } from ".";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const oidcClient = () => { export const oidcClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { BetterFetchOption } from "@better-fetch/fetch"; import type { BetterFetchOption } from "@better-fetch/fetch";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
declare global { declare global {
interface Window { interface Window {

View File

@@ -1,5 +1,5 @@
import type { oneTimeToken } from "./index"; import type { oneTimeToken } from "./index";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const oneTimeTokenClient = () => { export const oneTimeTokenClient = () => {
return { return {

View File

@@ -9,7 +9,7 @@ import type {
} from "../../plugins/organization/schema"; } from "../../plugins/organization/schema";
import type { Prettify } from "../../types/helper"; import type { Prettify } from "../../types/helper";
import { type AccessControl, type Role } from "../access"; import { type AccessControl, type Role } from "../access";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
import { organization } from "./organization"; import { organization } from "./organization";
import { useAuthQuery } from "../../client"; import { useAuthQuery } from "../../client";
import { import {

View File

@@ -11,7 +11,7 @@ import type {
import type { Session } from "inspector"; import type { Session } from "inspector";
import type { User } from "../../types"; import type { User } from "../../types";
import type { passkey as passkeyPl, Passkey } from "."; import type { passkey as passkeyPl, Passkey } from ".";
import type { BetterAuthClientPlugin, Store } from "../../client/types"; import type { BetterAuthClientPlugin, ClientStore } from "@better-auth/core";
import { useAuthQuery } from "../../client"; import { useAuthQuery } from "../../client";
import { atom } from "nanostores"; import { atom } from "nanostores";
@@ -22,7 +22,7 @@ export const getPasskeyActions = (
$store, $store,
}: { }: {
$listPasskeys: ReturnType<typeof atom<any>>; $listPasskeys: ReturnType<typeof atom<any>>;
$store: Store; $store: ClientStore;
}, },
) => { ) => {
const signInPasskey = async ( const signInPasskey = async (

View File

@@ -1,5 +1,5 @@
import type { phoneNumber } from "."; import type { phoneNumber } from ".";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const phoneNumberClient = () => { export const phoneNumberClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { siwe } from "."; import type { siwe } from ".";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const siweClient = () => { export const siweClient = () => {
return { return {

View File

@@ -1,5 +1,5 @@
import type { sso } from "."; import type { sso } from ".";
import type { BetterAuthClientPlugin } from "../../types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const ssoClient = () => { export const ssoClient = () => {
return { return {

View File

@@ -1,4 +1,4 @@
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
import type { twoFactor as twoFa } from "../../plugins/two-factor"; import type { twoFactor as twoFa } from "../../plugins/two-factor";
export const twoFactorClient = (options?: { export const twoFactorClient = (options?: {

View File

@@ -1,5 +1,5 @@
import type { username } from "."; import type { username } from ".";
import type { BetterAuthClientPlugin } from "../../client/types"; import type { BetterAuthClientPlugin } from "@better-auth/core";
export const usernameClient = () => { export const usernameClient = () => {
return { return {

View File

@@ -1,7 +1,7 @@
import { afterAll } from "vitest"; import { afterAll } from "vitest";
import { betterAuth } from "../auth"; import { betterAuth } from "../auth";
import { createAuthClient } from "../client/vanilla"; import { createAuthClient } from "../client/vanilla";
import type { BetterAuthOptions, ClientOptions, Session, User } from "../types"; import type { BetterAuthOptions, Session, User } from "../types";
import { getMigrations } from "../db/get-migration"; import { getMigrations } from "../db/get-migration";
import { parseSetCookieHeader, setCookieToHeader } from "../cookies"; import { parseSetCookieHeader, setCookieToHeader } from "../cookies";
import type { SuccessContext } from "@better-fetch/fetch"; import type { SuccessContext } from "@better-fetch/fetch";
@@ -13,10 +13,11 @@ import { MongoClient } from "mongodb";
import { mongodbAdapter } from "../adapters/mongodb-adapter"; import { mongodbAdapter } from "../adapters/mongodb-adapter";
import { createPool } from "mysql2/promise"; import { createPool } from "mysql2/promise";
import { bearer } from "../plugins"; import { bearer } from "../plugins";
import type { BetterAuthClientOptions } from "@better-auth/core";
export async function getTestInstanceMemory< export async function getTestInstanceMemory<
O extends Partial<BetterAuthOptions>, O extends Partial<BetterAuthOptions>,
C extends ClientOptions, C extends BetterAuthClientOptions,
>( >(
options?: O, options?: O,
config?: { config?: {

View File

@@ -2,7 +2,8 @@ import { AsyncLocalStorage } from "node:async_hooks";
import { afterAll } from "vitest"; import { afterAll } from "vitest";
import { betterAuth } from "../auth"; import { betterAuth } from "../auth";
import { createAuthClient } from "../client/vanilla"; import { createAuthClient } from "../client/vanilla";
import type { ClientOptions, Session, User } from "../types"; import type { Session, User } from "../types";
import type { BetterAuthClientOptions } from "@better-auth/core";
import { getMigrations } from "../db/get-migration"; import { getMigrations } from "../db/get-migration";
import { parseSetCookieHeader, setCookieToHeader } from "../cookies"; import { parseSetCookieHeader, setCookieToHeader } from "../cookies";
import type { SuccessContext } from "@better-fetch/fetch"; import type { SuccessContext } from "@better-fetch/fetch";
@@ -33,7 +34,7 @@ afterAll(async () => {
export async function getTestInstance< export async function getTestInstance<
O extends Partial<BetterAuthOptions>, O extends Partial<BetterAuthOptions>,
C extends ClientOptions, C extends BetterAuthClientOptions,
>( >(
options?: O, options?: O,
config?: { config?: {

View File

@@ -154,6 +154,7 @@
"better-sqlite3": "^12.4.1", "better-sqlite3": "^12.4.1",
"jose": "^6.1.0", "jose": "^6.1.0",
"kysely": "^0.28.5", "kysely": "^0.28.5",
"nanostores": "^1.0.1",
"unbuild": "catalog:" "unbuild": "catalog:"
}, },
"dependencies": { "dependencies": {
@@ -165,6 +166,7 @@
"better-call": "catalog:", "better-call": "catalog:",
"better-sqlite3": "^12.4.1", "better-sqlite3": "^12.4.1",
"jose": "^6.1.0", "jose": "^6.1.0",
"kysely": "^0.28.5" "kysely": "^0.28.5",
"nanostores": "^1.0.1"
} }
} }

View File

@@ -12,3 +12,9 @@ export type {
InternalAdapter, InternalAdapter,
} from "./context"; } from "./context";
export type { BetterAuthPlugin } from "./plugin"; export type { BetterAuthPlugin } from "./plugin";
export type {
BetterAuthClientPlugin,
BetterAuthClientOptions,
ClientStore,
ClientAtomListener,
} from "./plugin-client";

View File

@@ -0,0 +1,69 @@
import type { BetterAuthPlugin } from "./plugin";
import type {
BetterFetch,
BetterFetchOption,
BetterFetchPlugin,
} from "@better-fetch/fetch";
import type { LiteralString } from "./helper";
import type { BetterAuthOptions } from "./init-options";
import type { WritableAtom, Atom } from "nanostores";
export interface ClientStore {
notify: (signal: string) => void;
listen: (signal: string, listener: () => void) => void;
atoms: Record<string, WritableAtom<any>>;
}
export type ClientAtomListener = {
matcher: (path: string) => boolean;
signal: "$sessionSignal" | Omit<string, "$sessionSignal">;
};
export interface BetterAuthClientOptions {
fetchOptions?: BetterFetchOption;
plugins?: BetterAuthClientPlugin[];
baseURL?: string;
basePath?: string;
disableDefaultFetchPlugins?: boolean;
$InferAuth?: BetterAuthOptions;
}
export interface BetterAuthClientPlugin {
id: LiteralString;
/**
* only used for type inference. don't pass the
* actual plugin
*/
$InferServerPlugin?: BetterAuthPlugin;
/**
* Custom actions
*/
getActions?: (
$fetch: BetterFetch,
$store: ClientStore,
/**
* better-auth client options
*/
options: BetterAuthClientOptions | undefined,
) => Record<string, any>;
/**
* State atoms that'll be resolved by each framework
* auth store.
*/
getAtoms?: ($fetch: BetterFetch) => Record<string, Atom<any>>;
/**
* specify path methods for server plugin inferred
* endpoints to force a specific method.
*/
pathMethods?: Record<string, "POST" | "GET">;
/**
* Better fetch plugins
*/
fetchPlugins?: BetterFetchPlugin[];
/**
* a list of recaller based on a matcher function.
* The signal name needs to match a signal in this
* plugin or any plugin the user might have added.
*/
atomListeners?: ClientAtomListener[];
}

3
pnpm-lock.yaml generated
View File

@@ -1113,6 +1113,9 @@ importers:
kysely: kysely:
specifier: ^0.28.5 specifier: ^0.28.5
version: 0.28.5 version: 0.28.5
nanostores:
specifier: ^1.0.1
version: 1.0.1
unbuild: unbuild:
specifier: 'catalog:' specifier: 'catalog:'
version: 3.6.1(sass@1.90.0)(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2)) version: 3.6.1(sass@1.90.0)(typescript@5.9.2)(vue@3.5.19(typescript@5.9.2))