@convex-dev/better-auth 0.9.10 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/auth-config.d.ts +43 -0
  2. package/dist/auth-config.d.ts.map +1 -0
  3. package/dist/auth-config.js +45 -0
  4. package/dist/auth-config.js.map +1 -0
  5. package/dist/auth-options.d.ts +3 -0
  6. package/dist/auth-options.d.ts.map +1 -0
  7. package/dist/auth-options.js +41 -0
  8. package/dist/auth-options.js.map +1 -0
  9. package/dist/auth.d.ts +1 -3
  10. package/dist/auth.d.ts.map +1 -1
  11. package/dist/auth.js +2 -42
  12. package/dist/auth.js.map +1 -1
  13. package/dist/client/{adapterUtils.d.ts → adapter-utils.d.ts} +15 -15
  14. package/dist/client/adapter-utils.d.ts.map +1 -0
  15. package/dist/client/{adapterUtils.js → adapter-utils.js} +1 -1
  16. package/dist/client/adapter-utils.js.map +1 -0
  17. package/dist/client/adapter.d.ts +1 -2
  18. package/dist/client/adapter.d.ts.map +1 -1
  19. package/dist/client/adapter.js +4 -4
  20. package/dist/client/adapter.js.map +1 -1
  21. package/dist/client/create-api.d.ts +139 -0
  22. package/dist/client/create-api.d.ts.map +1 -0
  23. package/dist/client/create-api.js +204 -0
  24. package/dist/client/create-api.js.map +1 -0
  25. package/dist/client/create-client.d.ts +183 -0
  26. package/dist/client/create-client.d.ts.map +1 -0
  27. package/dist/client/create-client.js +311 -0
  28. package/dist/client/create-client.js.map +1 -0
  29. package/dist/client/{createSchema.d.ts → create-schema.d.ts} +1 -1
  30. package/dist/client/create-schema.d.ts.map +1 -0
  31. package/dist/client/{createSchema.js → create-schema.js} +11 -5
  32. package/dist/client/create-schema.js.map +1 -0
  33. package/dist/client/index.d.ts +4 -279
  34. package/dist/client/index.d.ts.map +1 -1
  35. package/dist/client/index.js +6 -476
  36. package/dist/client/index.js.map +1 -1
  37. package/dist/component/_generated/component.d.ts +0 -3
  38. package/dist/component/_generated/component.d.ts.map +1 -1
  39. package/dist/component/adapter.d.ts +19 -21
  40. package/dist/component/adapter.d.ts.map +1 -1
  41. package/dist/component/adapter.js +2 -2
  42. package/dist/component/adapter.js.map +1 -1
  43. package/dist/component/schema.d.ts +50 -50
  44. package/dist/nextjs/client.d.ts +4 -0
  45. package/dist/nextjs/client.d.ts.map +1 -0
  46. package/dist/nextjs/client.js +37 -0
  47. package/dist/nextjs/client.js.map +1 -0
  48. package/dist/nextjs/index.d.ts +19 -7
  49. package/dist/nextjs/index.d.ts.map +1 -1
  50. package/dist/nextjs/index.js +90 -36
  51. package/dist/nextjs/index.js.map +1 -1
  52. package/dist/plugins/convex/client.d.ts +1 -1
  53. package/dist/plugins/convex/client.d.ts.map +1 -1
  54. package/dist/plugins/convex/client.js +0 -1
  55. package/dist/plugins/convex/client.js.map +1 -1
  56. package/dist/plugins/convex/index.d.ts +239 -227
  57. package/dist/plugins/convex/index.d.ts.map +1 -1
  58. package/dist/plugins/convex/index.js +191 -37
  59. package/dist/plugins/convex/index.js.map +1 -1
  60. package/dist/plugins/cross-domain/client.d.ts +3 -3
  61. package/dist/plugins/cross-domain/client.d.ts.map +1 -1
  62. package/dist/plugins/cross-domain/index.d.ts +15 -70
  63. package/dist/plugins/cross-domain/index.d.ts.map +1 -1
  64. package/dist/plugins/cross-domain/index.js +8 -0
  65. package/dist/plugins/cross-domain/index.js.map +1 -1
  66. package/dist/react/index.d.ts +52 -2
  67. package/dist/react/index.d.ts.map +1 -1
  68. package/dist/react/index.js +133 -9
  69. package/dist/react/index.js.map +1 -1
  70. package/dist/react-start/index.d.ts +11 -41
  71. package/dist/react-start/index.d.ts.map +1 -1
  72. package/dist/react-start/index.js +82 -106
  73. package/dist/react-start/index.js.map +1 -1
  74. package/dist/utils/index.d.ts +20 -2
  75. package/dist/utils/index.d.ts.map +1 -1
  76. package/dist/utils/index.js +54 -1
  77. package/dist/utils/index.js.map +1 -1
  78. package/package.json +19 -12
  79. package/src/auth-config.ts +82 -0
  80. package/src/auth-options.ts +54 -0
  81. package/src/auth.ts +3 -56
  82. package/src/client/adapter.ts +5 -5
  83. package/src/client/create-api.ts +337 -0
  84. package/src/client/create-client.ts +446 -0
  85. package/src/client/{createSchema.ts → create-schema.ts} +10 -4
  86. package/src/client/index.ts +22 -786
  87. package/src/component/_generated/component.ts +0 -7
  88. package/src/component/adapter.ts +2 -3
  89. package/src/nextjs/client.tsx +52 -0
  90. package/src/nextjs/index.ts +138 -45
  91. package/src/plugins/convex/client.ts +1 -1
  92. package/src/plugins/convex/index.ts +337 -51
  93. package/src/plugins/cross-domain/index.ts +10 -2
  94. package/src/react/index.tsx +195 -9
  95. package/src/react-start/index.ts +126 -171
  96. package/src/test.ts +1 -1
  97. package/src/utils/index.ts +96 -1
  98. package/dist/client/adapterUtils.d.ts.map +0 -1
  99. package/dist/client/adapterUtils.js.map +0 -1
  100. package/dist/client/createSchema.d.ts.map +0 -1
  101. package/dist/client/createSchema.js.map +0 -1
  102. /package/src/client/{adapterUtils.ts → adapter-utils.ts} +0 -0
@@ -1028,13 +1028,6 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
1028
1028
  any,
1029
1029
  Name
1030
1030
  >;
1031
- migrationRemoveUserId: FunctionReference<
1032
- "mutation",
1033
- "internal",
1034
- { userId: string },
1035
- any,
1036
- Name
1037
- >;
1038
1031
  updateMany: FunctionReference<
1039
1032
  "mutation",
1040
1033
  "internal",
@@ -1,5 +1,5 @@
1
1
  import { createApi } from "../client/index.js";
2
- import { auth } from "../auth.js";
2
+ import { options } from "../auth-options.js";
3
3
  import schema from "./schema.js";
4
4
 
5
5
  export const {
@@ -10,5 +10,4 @@ export const {
10
10
  updateMany,
11
11
  deleteOne,
12
12
  deleteMany,
13
- migrationRemoveUserId,
14
- } = createApi(schema, () => auth);
13
+ } = createApi(schema, () => options);
@@ -0,0 +1,52 @@
1
+ import { type Preloaded, useConvexAuth, useQuery } from "convex/react";
2
+ import { type FunctionReference, makeFunctionReference } from "convex/server";
3
+ import { jsonToConvex } from "convex/values";
4
+ import { useEffect, useMemo, useState } from "react";
5
+
6
+ const useConvexPreloadedQuery = <Query extends FunctionReference<"query">>(
7
+ preloadedQuery: Preloaded<Query>,
8
+ { requireAuth = true }: { requireAuth?: boolean } = {}
9
+ ): Query["_returnType"] => {
10
+ const { isLoading, isAuthenticated } = useConvexAuth();
11
+ const [preloadExpired, setPreloadExpired] = useState(false);
12
+ useEffect(() => {
13
+ if (requireAuth && !isLoading && !isAuthenticated) {
14
+ setPreloadExpired(true);
15
+ }
16
+ }, [requireAuth, isLoading, isAuthenticated]);
17
+ const args = useMemo(
18
+ () => jsonToConvex(preloadedQuery._argsJSON),
19
+ [preloadedQuery._argsJSON]
20
+ ) as Query["_args"];
21
+ const preloadedResult = useMemo(
22
+ () => jsonToConvex(preloadedQuery._valueJSON),
23
+ [preloadedQuery._valueJSON]
24
+ );
25
+ const result = useQuery(
26
+ makeFunctionReference(preloadedQuery._name) as Query,
27
+ requireAuth && !isAuthenticated ? ("skip" as const) : args
28
+ );
29
+ useEffect(() => {
30
+ if (result !== undefined) {
31
+ setPreloadExpired(true);
32
+ }
33
+ }, [result]);
34
+ if (requireAuth) {
35
+ return preloadExpired ? result : preloadedResult;
36
+ }
37
+ return result === undefined ? preloadedResult : result;
38
+ };
39
+
40
+ export const usePreloadedAuthQuery = <Query extends FunctionReference<"query">>(
41
+ preloadedQuery: Preloaded<Query>
42
+ ): Query["_returnType"] | null => {
43
+ const { isLoading } = useConvexAuth();
44
+ const latestData = useConvexPreloadedQuery(preloadedQuery);
45
+ const [data, setData] = useState(latestData);
46
+ useEffect(() => {
47
+ if (!isLoading) {
48
+ setData(latestData);
49
+ }
50
+ }, [latestData, isLoading]);
51
+ return data;
52
+ };
@@ -1,56 +1,149 @@
1
- import { createCookieGetter } from "better-auth/cookies";
2
- import { JWT_COOKIE_NAME } from "../plugins/convex/index.js";
3
- import { type CreateAuth, getStaticAuth } from "../client/index.js";
4
- import type { GenericDataModel } from "convex/server";
1
+ import { stripIndent } from "common-tags";
2
+ import {
3
+ fetchAction,
4
+ fetchMutation,
5
+ fetchQuery,
6
+ preloadQuery,
7
+ } from "convex/nextjs";
8
+ import type { Preloaded } from "convex/react";
9
+ import {
10
+ type ArgsAndOptions,
11
+ type FunctionReference,
12
+ type FunctionReturnType,
13
+ } from "convex/server";
14
+ import React from "react";
15
+ import { getToken, type GetTokenOptions } from "../utils/index.js";
16
+ import type { EmptyObject } from "convex-helpers";
5
17
 
6
- export const getToken = async <DataModel extends GenericDataModel>(
7
- createAuth: CreateAuth<DataModel>
8
- ) => {
9
- const options = getStaticAuth(createAuth).options;
10
- const { cookies } = await import("next/headers.js");
11
- const cookieStore = await cookies();
12
- const createCookie = createCookieGetter(options);
13
- const cookie = createCookie(JWT_COOKIE_NAME);
14
- const tokenCookie = cookieStore.get(cookie.name);
15
-
16
- // Warn if there's a secure cookie mismatch between Convex and Next.js
17
- if (!tokenCookie?.value) {
18
- const isSecure = cookie.name.startsWith("__Secure-");
19
- const insecureCookieName = cookie.name.replace("__Secure-", "");
20
- const insecureCookie = cookieStore.get(insecureCookieName);
21
- const secureCookieName = isSecure
22
- ? cookie.name
23
- : `__Secure-${insecureCookieName}`;
24
- const secureCookie = cookieStore.get(secureCookieName);
25
- if (isSecure && insecureCookie) {
26
- console.warn(
27
- `Looking for secure cookie ${cookie.name} but found insecure cookie ${insecureCookie.name}`
28
- );
29
- }
30
- if (!isSecure && secureCookie) {
31
- console.warn(
32
- `Looking for insecure cookie ${cookie.name} but found secure cookie ${secureCookie.name}`
33
- );
34
- }
18
+ // Caching supported for React 19+ only
19
+ const cache =
20
+ React.cache ||
21
+ ((fn: (...args: any[]) => any) => {
22
+ return (...args: any[]) => fn(...args);
23
+ });
24
+
25
+ const parseConvexSiteUrl = (url: string) => {
26
+ if (!url) {
27
+ throw new Error(stripIndent`
28
+ CONVEX_SITE_URL is not set.
29
+ This is automatically set in the Convex backend, but must be set in the Next.js environment.
30
+ For local development, this can be set in the .env.local file.
31
+ `);
32
+ }
33
+ if (url.endsWith(".convex.cloud")) {
34
+ throw new Error(stripIndent`
35
+ CONVEX_SITE_URL should be set to your Convex Site URL, which ends in .convex.site.
36
+ Currently set to ${url}.
37
+ `);
35
38
  }
36
- return tokenCookie?.value;
39
+ return url;
37
40
  };
38
41
 
39
- const handler = (request: Request, opts?: { convexSiteUrl?: string }) => {
42
+ const handler = (request: Request, siteUrl: string) => {
40
43
  const requestUrl = new URL(request.url);
41
- const convexSiteUrl =
42
- opts?.convexSiteUrl ?? process.env.NEXT_PUBLIC_CONVEX_SITE_URL;
43
- if (!convexSiteUrl) {
44
- throw new Error("NEXT_PUBLIC_CONVEX_SITE_URL is not set");
45
- }
46
- const nextUrl = `${convexSiteUrl}${requestUrl.pathname}${requestUrl.search}`;
44
+ const nextUrl = `${siteUrl}${requestUrl.pathname}${requestUrl.search}`;
47
45
  const newRequest = new Request(nextUrl, request);
48
46
  newRequest.headers.set("accept-encoding", "application/json");
49
- newRequest.headers.set("host", convexSiteUrl);
47
+ newRequest.headers.set("host", siteUrl);
50
48
  return fetch(newRequest, { method: request.method, redirect: "manual" });
51
49
  };
52
50
 
53
- export const nextJsHandler = (opts?: { convexSiteUrl?: string }) => ({
54
- GET: (request: Request) => handler(request, opts),
55
- POST: (request: Request) => handler(request, opts),
51
+ const nextJsHandler = (siteUrl: string) => ({
52
+ GET: (request: Request) => handler(request, siteUrl),
53
+ POST: (request: Request) => handler(request, siteUrl),
56
54
  });
55
+
56
+ type OptionalArgs<FuncRef extends FunctionReference<any, any>> =
57
+ FuncRef["_args"] extends EmptyObject
58
+ ? [args?: EmptyObject]
59
+ : [args: FuncRef["_args"]];
60
+
61
+ const getArgsAndOptions = <FuncRef extends FunctionReference<any, any>>(
62
+ args: OptionalArgs<FuncRef>,
63
+ token?: string
64
+ ): ArgsAndOptions<FuncRef, { token?: string }> => {
65
+ return [args[0], { token }];
66
+ };
67
+
68
+ export const convexBetterAuthNextJs = (
69
+ opts: GetTokenOptions & { convexUrl: string; convexSiteUrl: string }
70
+ ) => {
71
+ const siteUrl = parseConvexSiteUrl(opts.convexSiteUrl);
72
+
73
+ const cachedGetToken = cache(
74
+ async ({ forceRefresh }: { forceRefresh?: boolean } = {}) => {
75
+ const headers = await (await import("next/headers.js")).headers();
76
+ return getToken(siteUrl, headers, { ...opts, forceRefresh });
77
+ }
78
+ );
79
+
80
+ const callWithToken = async <
81
+ FnType extends "query" | "mutation" | "action",
82
+ Fn extends FunctionReference<FnType>,
83
+ >(
84
+ fn: (token?: string) => Promise<FunctionReturnType<Fn>>
85
+ ): Promise<FunctionReturnType<Fn>> => {
86
+ const token = await cachedGetToken();
87
+ try {
88
+ return await fn(token?.token);
89
+ } catch (error) {
90
+ if (
91
+ !opts?.jwtCache?.enabled ||
92
+ token.isFresh ||
93
+ opts.jwtCache.isAuthError(error)
94
+ ) {
95
+ throw error;
96
+ }
97
+ const newToken = await cachedGetToken({ forceRefresh: true });
98
+ return await fn(newToken.token);
99
+ }
100
+ };
101
+
102
+ return {
103
+ getToken: async () => {
104
+ const token = await cachedGetToken();
105
+ return token.token;
106
+ },
107
+ handler: nextJsHandler(siteUrl),
108
+ isAuthenticated: async () => {
109
+ const token = await cachedGetToken();
110
+ return !!token.token;
111
+ },
112
+ preloadAuthQuery: async <Query extends FunctionReference<"query">>(
113
+ query: Query,
114
+ ...args: OptionalArgs<Query>
115
+ ): Promise<Preloaded<Query>> => {
116
+ return callWithToken((token?: string) => {
117
+ const argsAndOptions = getArgsAndOptions(args, token);
118
+ return preloadQuery(query, ...argsAndOptions);
119
+ });
120
+ },
121
+ fetchAuthQuery: async <Query extends FunctionReference<"query">>(
122
+ query: Query,
123
+ ...args: OptionalArgs<Query>
124
+ ): Promise<FunctionReturnType<Query>> => {
125
+ return callWithToken((token?: string) => {
126
+ const argsAndOptions = getArgsAndOptions(args, token);
127
+ return fetchQuery(query, ...argsAndOptions);
128
+ });
129
+ },
130
+ fetchAuthMutation: async <Mutation extends FunctionReference<"mutation">>(
131
+ mutation: Mutation,
132
+ ...args: OptionalArgs<Mutation>
133
+ ): Promise<FunctionReturnType<Mutation>> => {
134
+ return callWithToken((token?: string) => {
135
+ const argsAndOptions = getArgsAndOptions(args, token);
136
+ return fetchMutation(mutation, ...argsAndOptions);
137
+ });
138
+ },
139
+ fetchAuthAction: async <Action extends FunctionReference<"action">>(
140
+ action: Action,
141
+ ...args: OptionalArgs<Action>
142
+ ): Promise<FunctionReturnType<Action>> => {
143
+ return callWithToken((token?: string) => {
144
+ const argsAndOptions = getArgsAndOptions(args, token);
145
+ return fetchAction(action, ...argsAndOptions);
146
+ });
147
+ },
148
+ };
149
+ };
@@ -1,5 +1,5 @@
1
1
  import type { BetterAuthClientPlugin } from "better-auth/client";
2
- import { convex } from "./index.js";
2
+ import type { convex } from "./index.js";
3
3
 
4
4
  export const convexClient = () => {
5
5
  return {