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/access.js +125 -1
- package/dist/adapters.d.ts +1 -2
- package/dist/adapters.js +1155 -14
- package/dist/api.js +2458 -4
- package/dist/cli.js +1010 -3
- package/dist/client/plugins.js +527 -2
- package/dist/client.js +382 -1
- package/dist/index.js +3670 -5
- package/dist/next-js.js +34 -1
- package/dist/node.js +8 -1
- package/dist/plugins.js +5529 -4
- package/dist/react.js +393 -1
- package/dist/social.js +586 -2
- package/dist/solid-start.js +13 -1
- package/dist/solid.js +389 -1
- package/dist/svelte-kit.js +34 -1
- package/dist/svelte.js +380 -1
- package/dist/utils.js +453 -2
- package/dist/vue.js +389 -1
- package/package.json +5 -3
package/dist/vue.js
CHANGED
|
@@ -1 +1,389 @@
|
|
|
1
|
-
|
|
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
|
+
"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
|
|
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",
|