better-auth 0.2.3-beta.8 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vue.js CHANGED
@@ -1 +1,389 @@
1
- import{useStore as E}from"@nanostores/vue";import{createFetch as q}from"@better-fetch/fetch";var f=class extends Error{constructor(t,s,i){super(t),this.name="BetterAuthError",this.message=t,this.cause=s}};function v(e){try{return new URL(e).pathname!=="/"}catch{throw new f(`Invalid base URL: ${e}. Please provide a valid base URL.`)}}function A(e,t="/api/auth"){return v(e)?e:(t=t.startsWith("/")?t:`/${t}`,`${e}${t}`)}function U(e,t){if(e)return A(e,t);let s=process?.env||{},i=s.BETTER_AUTH_URL||s.NEXT_PUBLIC_BETTER_AUTH_URL||s.PUBLIC_BETTER_AUTH_URL||s.NUXT_PUBLIC_BETTER_AUTH_URL||s.NUXT_PUBLIC_AUTH_URL||(s.BASE_URL!=="/"?s.BASE_URL:void 0);if(i)return A(i,t);if(typeof window<"u")return A(window.location.origin,t)}import"nanostores";import{betterFetch as $}from"@better-fetch/fetch";var B={id:"redirect",name:"Redirect",hooks:{onSuccess(e){e.data?.url&&e.data?.redirect&&typeof window<"u"&&(window.location.href=e.data.url)}}},b={id:"add-current-url",name:"Add current URL",hooks:{onRequest(e){if(typeof window<"u"){let t=new URL(e.url);t.searchParams.set("currentURL",window.location.href),e.url=t}return e}}},O=new Map,L={id:"csrf",name:"CSRF Check",async init(e,t){if(!t?.baseURL)throw new f("API Base URL on the auth client isn't configured. Please pass it directly to the client `baseURL`");if(t?.method!=="GET"){t=t||{};let s=O.get("CSRF_TOKEN");if(!s){let{data:i,error:r}=await $("/csrf",{body:void 0,baseURL:t.baseURL,plugins:[],method:"GET",credentials:"include",customFetchImpl:t.customFetchImpl});if(r){if(r.status===404)throw new f("CSRF route not found. Make sure the server is running and the base URL is correct and includes the path (e.g. http://localhost:3000/api/auth).");if(r.status===429)return new Response(JSON.stringify({message:"Too many requests. Please try again later."}),{status:429,statusText:"Too Many Requests"});throw new f("Failed to fetch CSRF token: "+r.message)}O.set("CSRF_TOKEN",i.csrfToken)}t.body={...t?.body,csrfToken:s}}return t.credentials="include",{url:e,options:t}}};var F=e=>{let t=q({baseURL:U(e?.fetchOptions?.baseURL||e?.baseURL),credentials:"include",...e?.fetchOptions,plugins:[L,B,b,...e?.fetchOptions?.plugins?.filter(n=>n!==void 0)||[],...e?.plugins?.flatMap(n=>n.fetchPlugins).filter(n=>n!==void 0)||[]]}),s=e?.plugins||[],i={},r={},c={"/sign-out":"POST","/user/revoke-sessions":"POST"},o=[{signal:"_sessionSignal",matcher(n){return n==="/sign-out"||n==="sign-up/email"||n==="/user/update"}}];for(let n of s)n.getActions&&Object.assign(i,n.getActions?.(t)),n.getAtoms&&Object.assign(r,n.getAtoms?.(t)),n.pathMethods&&Object.assign(c,n.pathMethods),n.atomListeners&&o.push(...n.atomListeners);return{pluginsActions:i,pluginsAtoms:r,pluginPathMethods:c,atomListeners:o,$fetch:t}};function S(e){return e.charAt(0).toUpperCase()+e.slice(1)}function W(e,t,s){let i=t[e],{fetchOptions:r,query:c,...o}=s||{};return i||(r?.method?r.method:o&&Object.keys(o).length>0?"POST":"GET")}function C(e,t,s,i,r){function c(o=[]){return new Proxy(function(){},{get(n,u){let l=[...o,u],a=e;for(let d of l)if(a&&typeof a=="object"&&d in a)a=a[d];else{a=void 0;break}return typeof a=="function"?a:c(l)},apply:async(n,u,l)=>{let a="/"+o.map(y=>y.replace(/[A-Z]/g,g=>`-${g.toLowerCase()}`)).join("/"),d=l[0]||{},R=l[1]||{},{query:h,fetchOptions:m,...I}=d,p={...R,...m},T=W(a,s,d);return await t(a,{...p,body:T==="GET"?void 0:{...I,...p?.body||{}},query:h||p?.query,method:T,async onSuccess(y){await p?.onSuccess?.(y);let g=r?.find(k=>k.matcher(a));if(!g)return;let P=i[g.signal];if(!P)return;let _=P.get();setTimeout(()=>{P.set(!_)},10)}})}})}return c()}import{atom as j}from"nanostores";import"@better-fetch/fetch";import{atom as M,onMount as H}from"nanostores";var w=(e,t,s,i)=>{let r=M({data:null,error:null,isPending:!1}),c=()=>{let n=typeof i=="function"?i({data:r.get().data,error:r.get().error,isPending:r.get().isPending}):i;return s(t,{...n,onSuccess:async u=>{r.set({data:u.data,error:null,isPending:!1}),await n?.onSuccess?.(u)},async onError(u){r.set({error:u.error,data:null,isPending:!1}),await n?.onError?.(u)},async onRequest(u){let l=r.get();r.set({isPending:!0,data:l.data,error:l.error}),await n?.onRequest?.(u)}})};e=Array.isArray(e)?e:[e];let o=!1;for(let n of e)n.subscribe(()=>{o?c():H(r,()=>(c(),o=!0,()=>{r.off(),n.off()}))});return r};function x(e){let t=j(!1);return{$session:w(t,"/session",e,{method:"GET"}),_sessionSignal:t,$Infer:{}}}function G(e){return`use${S(e)}`}function he(e){let{pluginPathMethods:t,pluginsActions:s,pluginsAtoms:i,$fetch:r,atomListeners:c}=F(e),o={};for(let[h,m]of Object.entries(i))o[G(h)]=()=>E(m);let{$session:n,_sessionSignal:u,$Infer:l}=x(r);function a(){return E(n)}let d={...s,...o,useSession:a};return C(d,r,t,{...i,_sessionSignal:u},c)}export{he as createAuthClient};
1
+ // src/client/vue.ts
2
+ import { useStore } from "@nanostores/vue";
3
+
4
+ // src/client/config.ts
5
+ import { createFetch } from "@better-fetch/fetch";
6
+
7
+ // src/error/better-auth-error.ts
8
+ var BetterAuthError = class extends Error {
9
+ constructor(message, cause, docsLink) {
10
+ super(message);
11
+ this.name = "BetterAuthError";
12
+ this.message = message;
13
+ this.cause = cause;
14
+ this.stack = "";
15
+ }
16
+ };
17
+
18
+ // src/utils/base-url.ts
19
+ function checkHasPath(url) {
20
+ try {
21
+ const parsedUrl = new URL(url);
22
+ return parsedUrl.pathname !== "/";
23
+ } catch (error) {
24
+ throw new BetterAuthError(
25
+ `Invalid base URL: ${url}. Please provide a valid base URL.`
26
+ );
27
+ }
28
+ }
29
+ function withPath(url, path = "/api/auth") {
30
+ const hasPath = checkHasPath(url);
31
+ if (hasPath) {
32
+ return url;
33
+ }
34
+ path = path.startsWith("/") ? path : `/${path}`;
35
+ return `${url}${path}`;
36
+ }
37
+ function getBaseURL(url, path) {
38
+ if (url) {
39
+ return withPath(url, path);
40
+ }
41
+ const env = process?.env || {};
42
+ const fromEnv = env.BETTER_AUTH_URL || env.NEXT_PUBLIC_BETTER_AUTH_URL || env.PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_AUTH_URL || (env.BASE_URL !== "/" ? env.BASE_URL : void 0);
43
+ if (fromEnv) {
44
+ return withPath(fromEnv, path);
45
+ }
46
+ if (typeof window !== "undefined") {
47
+ return withPath(window.location.origin, path);
48
+ }
49
+ return void 0;
50
+ }
51
+
52
+ // src/client/config.ts
53
+ import "nanostores";
54
+
55
+ // src/client/fetch-plugins.ts
56
+ import { betterFetch } from "@better-fetch/fetch";
57
+ var redirectPlugin = {
58
+ id: "redirect",
59
+ name: "Redirect",
60
+ hooks: {
61
+ onSuccess(context) {
62
+ if (context.data?.url && context.data?.redirect) {
63
+ if (typeof window !== "undefined") {
64
+ window.location.href = context.data.url;
65
+ }
66
+ }
67
+ }
68
+ }
69
+ };
70
+ var addCurrentURL = {
71
+ id: "add-current-url",
72
+ name: "Add current URL",
73
+ hooks: {
74
+ onRequest(context) {
75
+ if (typeof window !== "undefined") {
76
+ const url = new URL(context.url);
77
+ url.searchParams.set("currentURL", window.location.href);
78
+ context.url = url;
79
+ }
80
+ return context;
81
+ }
82
+ }
83
+ };
84
+ var cache = /* @__PURE__ */ new Map();
85
+ var csrfPlugin = {
86
+ id: "csrf",
87
+ name: "CSRF Check",
88
+ async init(url, options) {
89
+ if (!options?.baseURL) {
90
+ throw new BetterAuthError(
91
+ "API Base URL on the auth client isn't configured. Please pass it directly to the client `baseURL`"
92
+ );
93
+ }
94
+ if (options?.method !== "GET") {
95
+ options = options || {};
96
+ const csrfToken = cache.get("CSRF_TOKEN");
97
+ if (!csrfToken) {
98
+ const { data, error } = await betterFetch("/csrf", {
99
+ body: void 0,
100
+ baseURL: options.baseURL,
101
+ plugins: [],
102
+ method: "GET",
103
+ credentials: "include",
104
+ customFetchImpl: options.customFetchImpl
105
+ });
106
+ if (error) {
107
+ if (error.status === 404) {
108
+ throw new BetterAuthError(
109
+ "CSRF route not found. Make sure the server is running and the base URL is correct and includes the path (e.g. http://localhost:3000/api/auth)."
110
+ );
111
+ }
112
+ if (error.status === 429) {
113
+ return new Response(
114
+ JSON.stringify({
115
+ message: "Too many requests. Please try again later."
116
+ }),
117
+ {
118
+ status: 429,
119
+ statusText: "Too Many Requests"
120
+ }
121
+ );
122
+ }
123
+ throw new BetterAuthError(
124
+ "Failed to fetch CSRF token: " + error.message
125
+ );
126
+ }
127
+ cache.set("CSRF_TOKEN", data.csrfToken);
128
+ }
129
+ options.body = {
130
+ ...options?.body,
131
+ csrfToken
132
+ };
133
+ }
134
+ options.credentials = "include";
135
+ return { url, options };
136
+ }
137
+ };
138
+
139
+ // src/client/config.ts
140
+ var getClientConfig = (options) => {
141
+ const $fetch = createFetch({
142
+ baseURL: getBaseURL(options?.fetchOptions?.baseURL || options?.baseURL),
143
+ credentials: "include",
144
+ ...options?.fetchOptions,
145
+ plugins: [
146
+ csrfPlugin,
147
+ redirectPlugin,
148
+ addCurrentURL,
149
+ ...options?.fetchOptions?.plugins?.filter((pl) => pl !== void 0) || [],
150
+ ...options?.plugins?.flatMap((plugin) => plugin.fetchPlugins).filter((pl) => pl !== void 0) || []
151
+ ]
152
+ });
153
+ const plugins = options?.plugins || [];
154
+ let pluginsActions = {};
155
+ let pluginsAtoms = {};
156
+ let pluginPathMethods = {
157
+ "/sign-out": "POST",
158
+ "/user/revoke-sessions": "POST"
159
+ };
160
+ const atomListeners = [
161
+ {
162
+ signal: "_sessionSignal",
163
+ matcher(path) {
164
+ return path === "/sign-out" || path === "sign-up/email" || path === "/user/update";
165
+ }
166
+ }
167
+ ];
168
+ for (const plugin of plugins) {
169
+ if (plugin.getActions) {
170
+ Object.assign(pluginsActions, plugin.getActions?.($fetch));
171
+ }
172
+ if (plugin.getAtoms) {
173
+ Object.assign(pluginsAtoms, plugin.getAtoms?.($fetch));
174
+ }
175
+ if (plugin.pathMethods) {
176
+ Object.assign(pluginPathMethods, plugin.pathMethods);
177
+ }
178
+ if (plugin.atomListeners) {
179
+ atomListeners.push(...plugin.atomListeners);
180
+ }
181
+ }
182
+ return {
183
+ pluginsActions,
184
+ pluginsAtoms,
185
+ pluginPathMethods,
186
+ atomListeners,
187
+ $fetch
188
+ };
189
+ };
190
+
191
+ // src/utils/misc.ts
192
+ function capitalizeFirstLetter(str) {
193
+ return str.charAt(0).toUpperCase() + str.slice(1);
194
+ }
195
+
196
+ // src/client/proxy.ts
197
+ function getMethod(path, knownPathMethods, args) {
198
+ const method = knownPathMethods[path];
199
+ const { fetchOptions, query, ...body } = args || {};
200
+ if (method) {
201
+ return method;
202
+ }
203
+ if (fetchOptions?.method) {
204
+ return fetchOptions.method;
205
+ }
206
+ if (body && Object.keys(body).length > 0) {
207
+ return "POST";
208
+ }
209
+ return "GET";
210
+ }
211
+ function createDynamicPathProxy(routes, client, knownPathMethods, atoms, atomListeners) {
212
+ function createProxy(path = []) {
213
+ return new Proxy(function() {
214
+ }, {
215
+ get(target, prop) {
216
+ const fullPath = [...path, prop];
217
+ let current = routes;
218
+ for (const segment of fullPath) {
219
+ if (current && typeof current === "object" && segment in current) {
220
+ current = current[segment];
221
+ } else {
222
+ current = void 0;
223
+ break;
224
+ }
225
+ }
226
+ if (typeof current === "function") {
227
+ return current;
228
+ }
229
+ return createProxy(fullPath);
230
+ },
231
+ apply: async (_, __, args) => {
232
+ const routePath = "/" + path.map(
233
+ (segment) => segment.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)
234
+ ).join("/");
235
+ const arg = args[0] || {};
236
+ const fetchOptions = args[1] || {};
237
+ const { query, fetchOptions: argFetchOptions, ...body } = arg;
238
+ const options = {
239
+ ...fetchOptions,
240
+ ...argFetchOptions
241
+ };
242
+ const method = getMethod(routePath, knownPathMethods, arg);
243
+ return await client(routePath, {
244
+ ...options,
245
+ body: method === "GET" ? void 0 : {
246
+ ...body,
247
+ ...options?.body || {}
248
+ },
249
+ query: query || options?.query,
250
+ method,
251
+ async onSuccess(context) {
252
+ await options?.onSuccess?.(context);
253
+ const matches = atomListeners?.find((s) => s.matcher(routePath));
254
+ if (!matches) return;
255
+ const signal = atoms[matches.signal];
256
+ if (!signal) return;
257
+ const val = signal.get();
258
+ setTimeout(() => {
259
+ signal.set(!val);
260
+ }, 10);
261
+ }
262
+ });
263
+ }
264
+ });
265
+ }
266
+ return createProxy();
267
+ }
268
+
269
+ // src/client/session-atom.ts
270
+ import { atom as atom2 } from "nanostores";
271
+
272
+ // src/client/query.ts
273
+ import "@better-fetch/fetch";
274
+ import { atom, onMount } from "nanostores";
275
+ var useAuthQuery = (initializedAtom, path, $fetch, options) => {
276
+ const value = atom({
277
+ data: null,
278
+ error: null,
279
+ isPending: false
280
+ });
281
+ const fn = () => {
282
+ const opts = typeof options === "function" ? options({
283
+ data: value.get().data,
284
+ error: value.get().error,
285
+ isPending: value.get().isPending
286
+ }) : options;
287
+ return $fetch(path, {
288
+ ...opts,
289
+ onSuccess: async (context) => {
290
+ value.set({
291
+ data: context.data,
292
+ error: null,
293
+ isPending: false
294
+ });
295
+ await opts?.onSuccess?.(context);
296
+ },
297
+ async onError(context) {
298
+ value.set({
299
+ error: context.error,
300
+ data: null,
301
+ isPending: false
302
+ });
303
+ await opts?.onError?.(context);
304
+ },
305
+ async onRequest(context) {
306
+ const currentValue = value.get();
307
+ value.set({
308
+ isPending: true,
309
+ data: currentValue.data,
310
+ error: currentValue.error
311
+ });
312
+ await opts?.onRequest?.(context);
313
+ }
314
+ });
315
+ };
316
+ initializedAtom = Array.isArray(initializedAtom) ? initializedAtom : [initializedAtom];
317
+ let isMounted = false;
318
+ for (const initAtom of initializedAtom) {
319
+ initAtom.subscribe(() => {
320
+ if (isMounted) {
321
+ fn();
322
+ } else {
323
+ onMount(value, () => {
324
+ fn();
325
+ isMounted = true;
326
+ return () => {
327
+ value.off();
328
+ initAtom.off();
329
+ };
330
+ });
331
+ }
332
+ });
333
+ }
334
+ return value;
335
+ };
336
+
337
+ // src/client/session-atom.ts
338
+ function getSessionAtom($fetch) {
339
+ const $signal = atom2(false);
340
+ const session = useAuthQuery($signal, "/session", $fetch, {
341
+ method: "GET"
342
+ });
343
+ return {
344
+ $session: session,
345
+ _sessionSignal: $signal,
346
+ $Infer: {}
347
+ };
348
+ }
349
+
350
+ // src/client/vue.ts
351
+ function getAtomKey(str) {
352
+ return `use${capitalizeFirstLetter(str)}`;
353
+ }
354
+ function createAuthClient(options) {
355
+ const {
356
+ pluginPathMethods,
357
+ pluginsActions,
358
+ pluginsAtoms,
359
+ $fetch,
360
+ atomListeners
361
+ } = getClientConfig(options);
362
+ let resolvedHooks = {};
363
+ for (const [key, value] of Object.entries(pluginsAtoms)) {
364
+ resolvedHooks[getAtomKey(key)] = () => useStore(value);
365
+ }
366
+ const { $session, _sessionSignal, $Infer } = getSessionAtom($fetch);
367
+ function useSession() {
368
+ return useStore($session);
369
+ }
370
+ const routes = {
371
+ ...pluginsActions,
372
+ ...resolvedHooks,
373
+ useSession
374
+ };
375
+ const proxy = createDynamicPathProxy(
376
+ routes,
377
+ $fetch,
378
+ pluginPathMethods,
379
+ {
380
+ ...pluginsAtoms,
381
+ _sessionSignal
382
+ },
383
+ atomListeners
384
+ );
385
+ return proxy;
386
+ }
387
+ export {
388
+ createAuthClient
389
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-auth",
3
- "version": "0.2.3-beta.8",
3
+ "version": "0.2.4",
4
4
  "description": "The most comprehensive authentication library for TypeScript.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -65,6 +65,7 @@
65
65
  "vue": "^3.5.0"
66
66
  },
67
67
  "dependencies": {
68
+ "@antfu/ni": "^0.23.0",
68
69
  "@babel/preset-react": "^7.24.7",
69
70
  "@babel/preset-typescript": "^7.24.7",
70
71
  "@better-fetch/fetch": "^1.1.9",
@@ -75,7 +76,6 @@
75
76
  "@nanostores/solid": "^0.4.2",
76
77
  "@nanostores/vue": "^0.10.0",
77
78
  "@noble/ciphers": "^0.6.0",
78
- "prettier": "^3.3.3",
79
79
  "@noble/hashes": "^1.4.0",
80
80
  "@oslojs/encoding": "^1.0.0",
81
81
  "@paralleldrive/cuid2": "^2.2.2",
@@ -89,6 +89,7 @@
89
89
  "consola": "^3.2.3",
90
90
  "defu": "^6.1.4",
91
91
  "dotenv": "^16.4.5",
92
+ "execa": "^9.4.0",
92
93
  "jose": "^5.7.0",
93
94
  "kysely": "^0.27.4",
94
95
  "nanoquery": "^1.3.0",
@@ -96,6 +97,7 @@
96
97
  "oauth4webapi": "^2.12.0",
97
98
  "ora": "^8.0.1",
98
99
  "oslo": "^1.2.1",
100
+ "prettier": "^3.3.3",
99
101
  "prompts": "^2.4.2",
100
102
  "zod": "^3.22.5"
101
103
  },
@@ -104,7 +106,7 @@
104
106
  "./package.json"
105
107
  ],
106
108
  "scripts": {
107
- "build": "NODE_OPTIONS='--max-old-space-size=16384' tsup --clean --minify",
109
+ "build": "NODE_OPTIONS='--max-old-space-size=16384' tsup --clean",
108
110
  "dev": "NODE_OPTIONS='--max-old-space-size=16384' tsup --watch --sourcemap",
109
111
  "test": "pnpm prisma:push && pnpm typecheck && vitest",
110
112
  "prisma:push": "prisma db push --schema src/adapters/prisma-adapter/test/schema.prisma",