@convex-dev/better-auth 0.9.11 → 0.10.1
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/auth-config.d.ts +43 -0
- package/dist/auth-config.d.ts.map +1 -0
- package/dist/auth-config.js +45 -0
- package/dist/auth-config.js.map +1 -0
- package/dist/auth-options.d.ts +3 -0
- package/dist/auth-options.d.ts.map +1 -0
- package/dist/auth-options.js +41 -0
- package/dist/auth-options.js.map +1 -0
- package/dist/auth.d.ts +1 -3
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -42
- package/dist/auth.js.map +1 -1
- package/dist/client/{adapterUtils.d.ts → adapter-utils.d.ts} +15 -15
- package/dist/client/adapter-utils.d.ts.map +1 -0
- package/dist/client/{adapterUtils.js → adapter-utils.js} +1 -1
- package/dist/client/adapter-utils.js.map +1 -0
- package/dist/client/adapter.d.ts +1 -2
- package/dist/client/adapter.d.ts.map +1 -1
- package/dist/client/adapter.js +4 -4
- package/dist/client/adapter.js.map +1 -1
- package/dist/client/create-api.d.ts +139 -0
- package/dist/client/create-api.d.ts.map +1 -0
- package/dist/client/create-api.js +204 -0
- package/dist/client/create-api.js.map +1 -0
- package/dist/client/create-client.d.ts +183 -0
- package/dist/client/create-client.d.ts.map +1 -0
- package/dist/client/create-client.js +311 -0
- package/dist/client/create-client.js.map +1 -0
- package/dist/client/{createSchema.d.ts → create-schema.d.ts} +1 -1
- package/dist/client/create-schema.d.ts.map +1 -0
- package/dist/client/{createSchema.js → create-schema.js} +11 -5
- package/dist/client/create-schema.js.map +1 -0
- package/dist/client/index.d.ts +4 -279
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -463
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/component.d.ts +0 -3
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/adapter.d.ts +19 -21
- package/dist/component/adapter.d.ts.map +1 -1
- package/dist/component/adapter.js +2 -2
- package/dist/component/adapter.js.map +1 -1
- package/dist/component/schema.d.ts +50 -50
- package/dist/nextjs/client.d.ts +4 -0
- package/dist/nextjs/client.d.ts.map +1 -0
- package/dist/nextjs/client.js +37 -0
- package/dist/nextjs/client.js.map +1 -0
- package/dist/nextjs/index.d.ts +19 -7
- package/dist/nextjs/index.d.ts.map +1 -1
- package/dist/nextjs/index.js +90 -36
- package/dist/nextjs/index.js.map +1 -1
- package/dist/plugins/convex/client.d.ts +1 -1
- package/dist/plugins/convex/client.d.ts.map +1 -1
- package/dist/plugins/convex/client.js +0 -1
- package/dist/plugins/convex/client.js.map +1 -1
- package/dist/plugins/convex/index.d.ts +239 -227
- package/dist/plugins/convex/index.d.ts.map +1 -1
- package/dist/plugins/convex/index.js +191 -37
- package/dist/plugins/convex/index.js.map +1 -1
- package/dist/plugins/cross-domain/client.d.ts +3 -3
- package/dist/plugins/cross-domain/client.d.ts.map +1 -1
- package/dist/plugins/cross-domain/index.d.ts +15 -70
- package/dist/plugins/cross-domain/index.d.ts.map +1 -1
- package/dist/plugins/cross-domain/index.js +8 -0
- package/dist/plugins/cross-domain/index.js.map +1 -1
- package/dist/react/index.d.ts +52 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +133 -9
- package/dist/react/index.js.map +1 -1
- package/dist/react-start/index.d.ts +11 -41
- package/dist/react-start/index.d.ts.map +1 -1
- package/dist/react-start/index.js +82 -106
- package/dist/react-start/index.js.map +1 -1
- package/dist/utils/index.d.ts +20 -2
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +54 -1
- package/dist/utils/index.js.map +1 -1
- package/package.json +20 -13
- package/src/auth-config.ts +82 -0
- package/src/auth-options.ts +54 -0
- package/src/auth.ts +3 -56
- package/src/client/adapter.ts +5 -5
- package/src/client/create-api.ts +337 -0
- package/src/client/create-client.ts +446 -0
- package/src/client/{createSchema.ts → create-schema.ts} +10 -4
- package/src/client/index.ts +22 -771
- package/src/component/_generated/component.ts +0 -7
- package/src/component/adapter.ts +2 -3
- package/src/nextjs/client.tsx +52 -0
- package/src/nextjs/index.ts +138 -45
- package/src/plugins/convex/client.ts +1 -1
- package/src/plugins/convex/index.ts +337 -51
- package/src/plugins/cross-domain/index.ts +10 -2
- package/src/react/index.tsx +195 -9
- package/src/react-start/index.ts +126 -171
- package/src/test.ts +1 -1
- package/src/utils/index.ts +96 -1
- package/dist/client/adapterUtils.d.ts.map +0 -1
- package/dist/client/adapterUtils.js.map +0 -1
- package/dist/client/createSchema.d.ts.map +0 -1
- package/dist/client/createSchema.js.map +0 -1
- /package/src/client/{adapterUtils.ts → adapter-utils.ts} +0 -0
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"bugs": {
|
|
7
7
|
"url": "https://github.com/get-convex/better-auth/issues"
|
|
8
8
|
},
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.10.1",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"keywords": [
|
|
12
12
|
"convex",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"dev": "cd examples/react && npm run dev",
|
|
22
22
|
"clean": "rm -rf dist *.tsbuildinfo",
|
|
23
|
-
"build": "tsc --project
|
|
23
|
+
"build": "tsc --project tsconfig.build.json",
|
|
24
24
|
"typecheck": "tsc --noEmit && tsc -p examples/next && tsc -p examples/next/convex && tsc -p examples/react && tsc -p examples/react/convex && tsc -p examples/tanstack && tsc -p examples/tanstack/convex",
|
|
25
25
|
"lint": "eslint .",
|
|
26
26
|
"all": "run-p -r 'dev' 'test:watch'",
|
|
@@ -59,6 +59,10 @@
|
|
|
59
59
|
"types": "./dist/component/adapter.d.ts",
|
|
60
60
|
"default": "./dist/component/adapter.js"
|
|
61
61
|
},
|
|
62
|
+
"./auth-config": {
|
|
63
|
+
"types": "./dist/auth-config.d.ts",
|
|
64
|
+
"default": "./dist/auth-config.js"
|
|
65
|
+
},
|
|
62
66
|
"./client/plugins": {
|
|
63
67
|
"types": "./dist/client/plugins/index.d.ts",
|
|
64
68
|
"default": "./dist/client/plugins/index.js"
|
|
@@ -67,6 +71,10 @@
|
|
|
67
71
|
"types": "./dist/nextjs/index.d.ts",
|
|
68
72
|
"default": "./dist/nextjs/index.js"
|
|
69
73
|
},
|
|
74
|
+
"./nextjs/client": {
|
|
75
|
+
"types": "./dist/nextjs/client.d.ts",
|
|
76
|
+
"default": "./dist/nextjs/client.js"
|
|
77
|
+
},
|
|
70
78
|
"./plugins": {
|
|
71
79
|
"types": "./dist/plugins/index.d.ts",
|
|
72
80
|
"default": "./dist/plugins/index.js"
|
|
@@ -93,26 +101,24 @@
|
|
|
93
101
|
}
|
|
94
102
|
},
|
|
95
103
|
"peerDependencies": {
|
|
96
|
-
"better-auth": "1.
|
|
104
|
+
"better-auth": "1.4.7",
|
|
97
105
|
"convex": "^1.25.0",
|
|
98
106
|
"react": "^18.3.1 || ^19.0.0",
|
|
99
107
|
"react-dom": "^18.3.1 || ^19.0.0"
|
|
100
108
|
},
|
|
101
109
|
"devDependencies": {
|
|
102
|
-
"@better-fetch/fetch": "^1.1.18",
|
|
103
110
|
"@edge-runtime/vm": "5.0.0",
|
|
104
111
|
"@eslint/eslintrc": "3.3.1",
|
|
105
112
|
"@eslint/js": "9.39.1",
|
|
106
|
-
"@tanstack/react-start": "^1.
|
|
113
|
+
"@tanstack/react-start": "^1.140.1",
|
|
107
114
|
"@types/common-tags": "^1.8.4",
|
|
108
115
|
"@types/node": "20.19.24",
|
|
109
116
|
"@types/react": "18.3.26",
|
|
110
117
|
"@types/react-dom": "18.3.7",
|
|
111
118
|
"@types/semver": "^7.7.0",
|
|
112
|
-
"@vitejs/plugin-react": "5.0.4",
|
|
113
|
-
"concurrently": "^9.2.0",
|
|
114
119
|
"chokidar-cli": "3.0.0",
|
|
115
|
-
"
|
|
120
|
+
"concurrently": "^9.2.0",
|
|
121
|
+
"convex": "^1.30.0",
|
|
116
122
|
"convex-test": "0.0.41",
|
|
117
123
|
"cpy-cli": "6.0.0",
|
|
118
124
|
"eslint": "9.39.1",
|
|
@@ -120,26 +126,27 @@
|
|
|
120
126
|
"eslint-plugin-react-hooks": "5.2.0",
|
|
121
127
|
"eslint-plugin-react-refresh": "0.4.24",
|
|
122
128
|
"globals": "15.14.0",
|
|
123
|
-
"next": "^
|
|
124
|
-
"npm-run-all2": "
|
|
129
|
+
"next": "^16.0.3",
|
|
130
|
+
"npm-run-all2": "8.0.4",
|
|
125
131
|
"pkg-pr-new": "0.0.60",
|
|
126
132
|
"prettier": "3.6.2",
|
|
127
133
|
"react": "18.3.1",
|
|
128
134
|
"react-dom": "18.3.1",
|
|
129
135
|
"typescript": "5.9.3",
|
|
130
136
|
"typescript-eslint": "8.46.4",
|
|
131
|
-
"
|
|
132
|
-
"vitest": "3.2.4"
|
|
137
|
+
"vitest": "^4.0.15"
|
|
133
138
|
},
|
|
134
139
|
"types": "./dist/client/index.d.ts",
|
|
135
140
|
"module": "./dist/client/index.js",
|
|
136
141
|
"dependencies": {
|
|
142
|
+
"@better-auth/passkey": "1.4.7",
|
|
143
|
+
"@better-fetch/fetch": "^1.1.18",
|
|
137
144
|
"common-tags": "^1.8.2",
|
|
138
145
|
"convex-helpers": "^0.1.95",
|
|
139
146
|
"jose": "^6.1.0",
|
|
140
147
|
"remeda": "^2.32.0",
|
|
141
148
|
"semver": "^7.7.3",
|
|
142
149
|
"type-fest": "^4.39.1",
|
|
143
|
-
"zod": "^
|
|
150
|
+
"zod": "^4.0.0"
|
|
144
151
|
}
|
|
145
152
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { JwtOptions } from "better-auth/plugins";
|
|
2
|
+
import type { AuthProvider } from "convex/server";
|
|
3
|
+
import type { JSONWebKeySet } from "jose";
|
|
4
|
+
|
|
5
|
+
type JwksDoc = {
|
|
6
|
+
id: string;
|
|
7
|
+
publicKey: string;
|
|
8
|
+
privateKey: string;
|
|
9
|
+
createdAt: number;
|
|
10
|
+
expiresAt?: number;
|
|
11
|
+
alg?: string;
|
|
12
|
+
crv?: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const createPublicJwks = (jwks: JwksDoc[], options?: JwtOptions) => {
|
|
16
|
+
/*
|
|
17
|
+
const now = Date.now();
|
|
18
|
+
const DEFAULT_GRACE_PERIOD = 60 * 60 * 24 * 30;
|
|
19
|
+
const gracePeriod =
|
|
20
|
+
(options?.jwks?.gracePeriod ?? DEFAULT_GRACE_PERIOD) * 1000;
|
|
21
|
+
|
|
22
|
+
const keys = jwks.filter((key) => {
|
|
23
|
+
if (!key.expiresAt) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
return new Date(key.expiresAt).getTime() + gracePeriod > now;
|
|
27
|
+
});
|
|
28
|
+
*/
|
|
29
|
+
const keys = jwks;
|
|
30
|
+
|
|
31
|
+
const keyPairConfig = options?.jwks?.keyPairConfig;
|
|
32
|
+
const defaultCrv = keyPairConfig
|
|
33
|
+
? "crv" in keyPairConfig
|
|
34
|
+
? (keyPairConfig as { crv: string }).crv
|
|
35
|
+
: undefined
|
|
36
|
+
: undefined;
|
|
37
|
+
return {
|
|
38
|
+
keys: keys.map((keySet) => {
|
|
39
|
+
return {
|
|
40
|
+
alg: keySet.alg ?? options?.jwks?.keyPairConfig?.alg ?? "EdDSA",
|
|
41
|
+
crv: keySet.crv ?? defaultCrv,
|
|
42
|
+
...JSON.parse(keySet.publicKey),
|
|
43
|
+
kid: keySet.id,
|
|
44
|
+
};
|
|
45
|
+
}),
|
|
46
|
+
} satisfies JSONWebKeySet as JSONWebKeySet;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const getAuthConfigProvider = (opts?: {
|
|
50
|
+
basePath?: string;
|
|
51
|
+
/**
|
|
52
|
+
* @param jwks - Optional static JWKS to avoid fetching from the database.
|
|
53
|
+
*
|
|
54
|
+
* This should be a stringified document from the Better Auth JWKS table. You
|
|
55
|
+
* can create one in the console.
|
|
56
|
+
*
|
|
57
|
+
* Example:
|
|
58
|
+
* ```bash
|
|
59
|
+
* npx convex run auth:generateJwk | npx convex env set JWKS
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* Then use it in your auth config:
|
|
63
|
+
* ```ts
|
|
64
|
+
* export default {
|
|
65
|
+
* providers: [getAuthConfigProvider({ jwks: process.env.JWKS })],
|
|
66
|
+
* } satisfies AuthConfig;
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
*/
|
|
70
|
+
jwks?: string;
|
|
71
|
+
}) => {
|
|
72
|
+
const parsedJwks = opts?.jwks ? JSON.parse(opts.jwks) : undefined;
|
|
73
|
+
return {
|
|
74
|
+
type: "customJwt",
|
|
75
|
+
issuer: `${process.env.CONVEX_SITE_URL}`,
|
|
76
|
+
applicationID: "convex",
|
|
77
|
+
algorithm: "RS256",
|
|
78
|
+
jwks: parsedJwks
|
|
79
|
+
? `data:text/plain;charset=utf-8;base64,${btoa(JSON.stringify(createPublicJwks(parsedJwks)))}`
|
|
80
|
+
: `${process.env.CONVEX_SITE_URL}${opts?.basePath ?? "/api/auth"}/convex/jwks`,
|
|
81
|
+
} satisfies AuthProvider;
|
|
82
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { type BetterAuthOptions } from "better-auth";
|
|
2
|
+
import {
|
|
3
|
+
anonymous,
|
|
4
|
+
bearer,
|
|
5
|
+
emailOTP,
|
|
6
|
+
genericOAuth,
|
|
7
|
+
jwt,
|
|
8
|
+
magicLink,
|
|
9
|
+
oidcProvider,
|
|
10
|
+
oneTap,
|
|
11
|
+
oneTimeToken,
|
|
12
|
+
phoneNumber,
|
|
13
|
+
twoFactor,
|
|
14
|
+
username,
|
|
15
|
+
} from "better-auth/plugins";
|
|
16
|
+
import { passkey } from "@better-auth/passkey";
|
|
17
|
+
import { convex } from "./plugins/convex/index.js";
|
|
18
|
+
import { convexAdapter } from "./client/adapter.js";
|
|
19
|
+
|
|
20
|
+
// This is the config used to generate the schema
|
|
21
|
+
export const options = {
|
|
22
|
+
database: convexAdapter({} as any, {} as any),
|
|
23
|
+
rateLimit: {
|
|
24
|
+
storage: "database",
|
|
25
|
+
},
|
|
26
|
+
plugins: [
|
|
27
|
+
twoFactor(),
|
|
28
|
+
anonymous(),
|
|
29
|
+
username(),
|
|
30
|
+
phoneNumber(),
|
|
31
|
+
magicLink({ sendMagicLink: async () => {} }),
|
|
32
|
+
emailOTP({ sendVerificationOTP: async () => {} }),
|
|
33
|
+
passkey(),
|
|
34
|
+
genericOAuth({
|
|
35
|
+
config: [
|
|
36
|
+
{
|
|
37
|
+
clientId: "",
|
|
38
|
+
clientSecret: "",
|
|
39
|
+
providerId: "",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
}),
|
|
43
|
+
oneTap(),
|
|
44
|
+
oidcProvider({
|
|
45
|
+
loginPage: "/login",
|
|
46
|
+
}),
|
|
47
|
+
bearer(),
|
|
48
|
+
oneTimeToken(),
|
|
49
|
+
jwt(),
|
|
50
|
+
convex({
|
|
51
|
+
authConfig: { providers: [{ applicationID: "convex", domain: "" }] },
|
|
52
|
+
}),
|
|
53
|
+
],
|
|
54
|
+
} as BetterAuthOptions; // assert type to avoid overloading ts compiler
|
package/src/auth.ts
CHANGED
|
@@ -1,57 +1,4 @@
|
|
|
1
|
-
import { betterAuth
|
|
2
|
-
import {
|
|
3
|
-
anonymous,
|
|
4
|
-
bearer,
|
|
5
|
-
emailOTP,
|
|
6
|
-
genericOAuth,
|
|
7
|
-
jwt,
|
|
8
|
-
magicLink,
|
|
9
|
-
oidcProvider,
|
|
10
|
-
oneTap,
|
|
11
|
-
oneTimeToken,
|
|
12
|
-
phoneNumber,
|
|
13
|
-
twoFactor,
|
|
14
|
-
username,
|
|
15
|
-
} from "better-auth/plugins";
|
|
16
|
-
import { convex } from "./plugins/convex/index.js";
|
|
17
|
-
import { passkey } from "better-auth/plugins/passkey";
|
|
18
|
-
import { convexAdapter } from "./client/adapter.js";
|
|
1
|
+
import { betterAuth } from "better-auth";
|
|
2
|
+
import { options } from "./auth-options.js";
|
|
19
3
|
|
|
20
|
-
|
|
21
|
-
const options = {
|
|
22
|
-
logger: {
|
|
23
|
-
disabled: true,
|
|
24
|
-
},
|
|
25
|
-
database: convexAdapter({} as any, {} as any),
|
|
26
|
-
rateLimit: {
|
|
27
|
-
storage: "database",
|
|
28
|
-
},
|
|
29
|
-
plugins: [
|
|
30
|
-
twoFactor(),
|
|
31
|
-
anonymous(),
|
|
32
|
-
username(),
|
|
33
|
-
phoneNumber(),
|
|
34
|
-
magicLink({ sendMagicLink: async () => {} }),
|
|
35
|
-
emailOTP({ sendVerificationOTP: async () => {} }),
|
|
36
|
-
passkey(),
|
|
37
|
-
genericOAuth({
|
|
38
|
-
config: [
|
|
39
|
-
{
|
|
40
|
-
clientId: "",
|
|
41
|
-
clientSecret: "",
|
|
42
|
-
providerId: "",
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
}),
|
|
46
|
-
oneTap(),
|
|
47
|
-
oidcProvider({
|
|
48
|
-
loginPage: "/login",
|
|
49
|
-
}),
|
|
50
|
-
bearer(),
|
|
51
|
-
oneTimeToken(),
|
|
52
|
-
jwt(),
|
|
53
|
-
convex(),
|
|
54
|
-
],
|
|
55
|
-
} as BetterAuthOptions; // assert type to avoid overloading ts compiler
|
|
56
|
-
const config = betterAuth(options) as ReturnType<typeof betterAuth>;
|
|
57
|
-
export { config as auth };
|
|
4
|
+
export const auth = betterAuth(options);
|
package/src/client/adapter.ts
CHANGED
|
@@ -105,7 +105,7 @@ export const convexAdapter = <
|
|
|
105
105
|
>(
|
|
106
106
|
ctx: Ctx,
|
|
107
107
|
api: {
|
|
108
|
-
adapter:
|
|
108
|
+
adapter: ComponentApi["adapter"];
|
|
109
109
|
adapterTest?: ComponentApi["adapterTest"];
|
|
110
110
|
},
|
|
111
111
|
config: {
|
|
@@ -130,10 +130,10 @@ export const convexAdapter = <
|
|
|
130
130
|
mapKeysTransformOutput: {
|
|
131
131
|
_id: "id",
|
|
132
132
|
},
|
|
133
|
-
//
|
|
134
|
-
// we convert them to numbers here. This aligns with how
|
|
135
|
-
// Convex stores _creationTime, and avoids a breaking change.
|
|
133
|
+
// Dates provided as strings
|
|
136
134
|
supportsDates: false,
|
|
135
|
+
// Convert dates to numbers. This aligns with how
|
|
136
|
+
// Convex stores _creationTime and avoids a breaking change.
|
|
137
137
|
customTransformInput: ({ data, fieldAttributes }) => {
|
|
138
138
|
if (data && fieldAttributes.type === "date") {
|
|
139
139
|
return new Date(data).getTime();
|
|
@@ -156,7 +156,7 @@ export const convexAdapter = <
|
|
|
156
156
|
isRunMutationCtx: isRunMutationCtx(ctx),
|
|
157
157
|
},
|
|
158
158
|
createSchema: async ({ file, tables }) => {
|
|
159
|
-
const { createSchema } = await import("./
|
|
159
|
+
const { createSchema } = await import("./create-schema.js");
|
|
160
160
|
return createSchema({ file, tables });
|
|
161
161
|
},
|
|
162
162
|
create: async ({ model, data, select }): Promise<any> => {
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FunctionHandle,
|
|
3
|
+
type SchemaDefinition,
|
|
4
|
+
mutationGeneric,
|
|
5
|
+
paginationOptsValidator,
|
|
6
|
+
queryGeneric,
|
|
7
|
+
} from "convex/server";
|
|
8
|
+
import { type GenericId, v } from "convex/values";
|
|
9
|
+
import { asyncMap } from "convex-helpers";
|
|
10
|
+
import { partial } from "convex-helpers/validators";
|
|
11
|
+
import {
|
|
12
|
+
adapterWhereValidator,
|
|
13
|
+
checkUniqueFields,
|
|
14
|
+
hasUniqueFields,
|
|
15
|
+
listOne,
|
|
16
|
+
paginate,
|
|
17
|
+
selectFields,
|
|
18
|
+
} from "./adapter-utils.js";
|
|
19
|
+
import { getAuthTables } from "better-auth/db";
|
|
20
|
+
import { type TableNames } from "../component/_generated/dataModel.js";
|
|
21
|
+
import type { BetterAuthOptions } from "better-auth";
|
|
22
|
+
|
|
23
|
+
const whereValidator = (
|
|
24
|
+
schema: SchemaDefinition<any, any>,
|
|
25
|
+
tableName: TableNames
|
|
26
|
+
) =>
|
|
27
|
+
v.object({
|
|
28
|
+
field: v.union(
|
|
29
|
+
...Object.keys(schema.tables[tableName].validator.fields).map((field) =>
|
|
30
|
+
v.literal(field)
|
|
31
|
+
),
|
|
32
|
+
v.literal("_id")
|
|
33
|
+
),
|
|
34
|
+
operator: v.optional(
|
|
35
|
+
v.union(
|
|
36
|
+
v.literal("lt"),
|
|
37
|
+
v.literal("lte"),
|
|
38
|
+
v.literal("gt"),
|
|
39
|
+
v.literal("gte"),
|
|
40
|
+
v.literal("eq"),
|
|
41
|
+
v.literal("in"),
|
|
42
|
+
v.literal("not_in"),
|
|
43
|
+
v.literal("ne"),
|
|
44
|
+
v.literal("contains"),
|
|
45
|
+
v.literal("starts_with"),
|
|
46
|
+
v.literal("ends_with")
|
|
47
|
+
)
|
|
48
|
+
),
|
|
49
|
+
value: v.union(
|
|
50
|
+
v.string(),
|
|
51
|
+
v.number(),
|
|
52
|
+
v.boolean(),
|
|
53
|
+
v.array(v.string()),
|
|
54
|
+
v.array(v.number()),
|
|
55
|
+
v.null()
|
|
56
|
+
),
|
|
57
|
+
connector: v.optional(v.union(v.literal("AND"), v.literal("OR"))),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const createApi = <Schema extends SchemaDefinition<any, any>>(
|
|
61
|
+
schema: Schema,
|
|
62
|
+
createAuthOptions: (ctx: any) => BetterAuthOptions
|
|
63
|
+
) => {
|
|
64
|
+
const betterAuthSchema = getAuthTables(createAuthOptions({} as any));
|
|
65
|
+
return {
|
|
66
|
+
create: mutationGeneric({
|
|
67
|
+
args: {
|
|
68
|
+
input: v.union(
|
|
69
|
+
...Object.entries(schema.tables).map(([model, table]) =>
|
|
70
|
+
v.object({
|
|
71
|
+
model: v.literal(model),
|
|
72
|
+
data: v.object((table as any).validator.fields),
|
|
73
|
+
})
|
|
74
|
+
)
|
|
75
|
+
),
|
|
76
|
+
select: v.optional(v.array(v.string())),
|
|
77
|
+
onCreateHandle: v.optional(v.string()),
|
|
78
|
+
},
|
|
79
|
+
handler: async (ctx, args) => {
|
|
80
|
+
await checkUniqueFields(
|
|
81
|
+
ctx,
|
|
82
|
+
schema,
|
|
83
|
+
betterAuthSchema,
|
|
84
|
+
args.input.model,
|
|
85
|
+
args.input.data
|
|
86
|
+
);
|
|
87
|
+
const id = await ctx.db.insert(
|
|
88
|
+
args.input.model as any,
|
|
89
|
+
args.input.data
|
|
90
|
+
);
|
|
91
|
+
const doc = await ctx.db.get(id);
|
|
92
|
+
if (!doc) {
|
|
93
|
+
throw new Error(`Failed to create ${args.input.model}`);
|
|
94
|
+
}
|
|
95
|
+
const result = selectFields(doc, args.select);
|
|
96
|
+
if (args.onCreateHandle) {
|
|
97
|
+
await ctx.runMutation(
|
|
98
|
+
args.onCreateHandle as FunctionHandle<"mutation">,
|
|
99
|
+
{
|
|
100
|
+
model: args.input.model,
|
|
101
|
+
doc,
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
return result;
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
findOne: queryGeneric({
|
|
109
|
+
args: {
|
|
110
|
+
model: v.union(
|
|
111
|
+
...Object.keys(schema.tables).map((model) => v.literal(model))
|
|
112
|
+
),
|
|
113
|
+
where: v.optional(v.array(adapterWhereValidator)),
|
|
114
|
+
select: v.optional(v.array(v.string())),
|
|
115
|
+
},
|
|
116
|
+
handler: async (ctx, args) => {
|
|
117
|
+
return await listOne(ctx, schema, betterAuthSchema, args);
|
|
118
|
+
},
|
|
119
|
+
}),
|
|
120
|
+
findMany: queryGeneric({
|
|
121
|
+
args: {
|
|
122
|
+
model: v.union(
|
|
123
|
+
...Object.keys(schema.tables).map((model) => v.literal(model))
|
|
124
|
+
),
|
|
125
|
+
where: v.optional(v.array(adapterWhereValidator)),
|
|
126
|
+
limit: v.optional(v.number()),
|
|
127
|
+
sortBy: v.optional(
|
|
128
|
+
v.object({
|
|
129
|
+
direction: v.union(v.literal("asc"), v.literal("desc")),
|
|
130
|
+
field: v.string(),
|
|
131
|
+
})
|
|
132
|
+
),
|
|
133
|
+
offset: v.optional(v.number()),
|
|
134
|
+
paginationOpts: paginationOptsValidator,
|
|
135
|
+
},
|
|
136
|
+
handler: async (ctx, args) => {
|
|
137
|
+
return await paginate(ctx, schema, betterAuthSchema, args);
|
|
138
|
+
},
|
|
139
|
+
}),
|
|
140
|
+
updateOne: mutationGeneric({
|
|
141
|
+
args: {
|
|
142
|
+
input: v.union(
|
|
143
|
+
...Object.entries(schema.tables).map(
|
|
144
|
+
([name, table]: [string, Schema["tables"][string]]) => {
|
|
145
|
+
const tableName = name as TableNames;
|
|
146
|
+
const fields = partial(table.validator.fields);
|
|
147
|
+
return v.object({
|
|
148
|
+
model: v.literal(tableName),
|
|
149
|
+
update: v.object(fields),
|
|
150
|
+
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
)
|
|
154
|
+
),
|
|
155
|
+
onUpdateHandle: v.optional(v.string()),
|
|
156
|
+
},
|
|
157
|
+
handler: async (ctx, args) => {
|
|
158
|
+
const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
|
|
159
|
+
if (!doc) {
|
|
160
|
+
throw new Error(`Failed to update ${args.input.model}`);
|
|
161
|
+
}
|
|
162
|
+
await checkUniqueFields(
|
|
163
|
+
ctx,
|
|
164
|
+
schema,
|
|
165
|
+
betterAuthSchema,
|
|
166
|
+
args.input.model,
|
|
167
|
+
args.input.update,
|
|
168
|
+
doc
|
|
169
|
+
);
|
|
170
|
+
await ctx.db.patch(
|
|
171
|
+
doc._id as GenericId<string>,
|
|
172
|
+
args.input.update as any
|
|
173
|
+
);
|
|
174
|
+
const updatedDoc = await ctx.db.get(doc._id as GenericId<string>);
|
|
175
|
+
if (!updatedDoc) {
|
|
176
|
+
throw new Error(`Failed to update ${args.input.model}`);
|
|
177
|
+
}
|
|
178
|
+
if (args.onUpdateHandle) {
|
|
179
|
+
await ctx.runMutation(
|
|
180
|
+
args.onUpdateHandle as FunctionHandle<"mutation">,
|
|
181
|
+
{
|
|
182
|
+
model: args.input.model,
|
|
183
|
+
newDoc: updatedDoc,
|
|
184
|
+
oldDoc: doc,
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
return updatedDoc;
|
|
189
|
+
},
|
|
190
|
+
}),
|
|
191
|
+
updateMany: mutationGeneric({
|
|
192
|
+
args: {
|
|
193
|
+
input: v.union(
|
|
194
|
+
...Object.entries(schema.tables).map(
|
|
195
|
+
([name, table]: [string, Schema["tables"][string]]) => {
|
|
196
|
+
const tableName = name as TableNames;
|
|
197
|
+
const fields = partial(table.validator.fields);
|
|
198
|
+
return v.object({
|
|
199
|
+
model: v.literal(tableName),
|
|
200
|
+
update: v.object(fields),
|
|
201
|
+
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
),
|
|
206
|
+
paginationOpts: paginationOptsValidator,
|
|
207
|
+
onUpdateHandle: v.optional(v.string()),
|
|
208
|
+
},
|
|
209
|
+
handler: async (ctx, args) => {
|
|
210
|
+
const { page, ...result } = await paginate(
|
|
211
|
+
ctx,
|
|
212
|
+
schema,
|
|
213
|
+
betterAuthSchema,
|
|
214
|
+
{
|
|
215
|
+
...args.input,
|
|
216
|
+
paginationOpts: args.paginationOpts,
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
if (args.input.update) {
|
|
220
|
+
if (
|
|
221
|
+
hasUniqueFields(
|
|
222
|
+
betterAuthSchema,
|
|
223
|
+
args.input.model,
|
|
224
|
+
args.input.update ?? {}
|
|
225
|
+
) &&
|
|
226
|
+
page.length > 1
|
|
227
|
+
) {
|
|
228
|
+
throw new Error(
|
|
229
|
+
`Attempted to set unique fields in multiple documents in ${args.input.model} with the same value. Fields: ${Object.keys(args.input.update ?? {}).join(", ")}`
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
await asyncMap(page, async (doc) => {
|
|
233
|
+
await checkUniqueFields(
|
|
234
|
+
ctx,
|
|
235
|
+
schema,
|
|
236
|
+
betterAuthSchema,
|
|
237
|
+
args.input.model,
|
|
238
|
+
args.input.update ?? {},
|
|
239
|
+
doc
|
|
240
|
+
);
|
|
241
|
+
await ctx.db.patch(
|
|
242
|
+
doc._id as GenericId<string>,
|
|
243
|
+
args.input.update as any
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
if (args.onUpdateHandle) {
|
|
247
|
+
await ctx.runMutation(
|
|
248
|
+
args.onUpdateHandle as FunctionHandle<"mutation">,
|
|
249
|
+
{
|
|
250
|
+
model: args.input.model,
|
|
251
|
+
newDoc: await ctx.db.get(doc._id as GenericId<string>),
|
|
252
|
+
oldDoc: doc,
|
|
253
|
+
}
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...result,
|
|
260
|
+
count: page.length,
|
|
261
|
+
ids: page.map((doc) => doc._id),
|
|
262
|
+
};
|
|
263
|
+
},
|
|
264
|
+
}),
|
|
265
|
+
deleteOne: mutationGeneric({
|
|
266
|
+
args: {
|
|
267
|
+
input: v.union(
|
|
268
|
+
...Object.keys(schema.tables).map((name: string) => {
|
|
269
|
+
const tableName = name as TableNames;
|
|
270
|
+
return v.object({
|
|
271
|
+
model: v.literal(tableName),
|
|
272
|
+
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
273
|
+
});
|
|
274
|
+
})
|
|
275
|
+
),
|
|
276
|
+
onDeleteHandle: v.optional(v.string()),
|
|
277
|
+
},
|
|
278
|
+
handler: async (ctx, args) => {
|
|
279
|
+
const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
|
|
280
|
+
if (!doc) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
await ctx.db.delete(doc._id as GenericId<string>);
|
|
284
|
+
if (args.onDeleteHandle) {
|
|
285
|
+
await ctx.runMutation(
|
|
286
|
+
args.onDeleteHandle as FunctionHandle<"mutation">,
|
|
287
|
+
{ model: args.input.model, doc }
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
return doc;
|
|
291
|
+
},
|
|
292
|
+
}),
|
|
293
|
+
deleteMany: mutationGeneric({
|
|
294
|
+
args: {
|
|
295
|
+
input: v.union(
|
|
296
|
+
...Object.keys(schema.tables).map((name: string) => {
|
|
297
|
+
const tableName = name as TableNames;
|
|
298
|
+
return v.object({
|
|
299
|
+
model: v.literal(tableName),
|
|
300
|
+
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
301
|
+
});
|
|
302
|
+
})
|
|
303
|
+
),
|
|
304
|
+
paginationOpts: paginationOptsValidator,
|
|
305
|
+
onDeleteHandle: v.optional(v.string()),
|
|
306
|
+
},
|
|
307
|
+
handler: async (ctx, args) => {
|
|
308
|
+
const { page, ...result } = await paginate(
|
|
309
|
+
ctx,
|
|
310
|
+
schema,
|
|
311
|
+
betterAuthSchema,
|
|
312
|
+
{
|
|
313
|
+
...args.input,
|
|
314
|
+
paginationOpts: args.paginationOpts,
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
await asyncMap(page, async (doc) => {
|
|
318
|
+
if (args.onDeleteHandle) {
|
|
319
|
+
await ctx.runMutation(
|
|
320
|
+
args.onDeleteHandle as FunctionHandle<"mutation">,
|
|
321
|
+
{
|
|
322
|
+
model: args.input.model,
|
|
323
|
+
doc,
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
await ctx.db.delete(doc._id as GenericId<string>);
|
|
328
|
+
});
|
|
329
|
+
return {
|
|
330
|
+
...result,
|
|
331
|
+
count: page.length,
|
|
332
|
+
ids: page.map((doc) => doc._id),
|
|
333
|
+
};
|
|
334
|
+
},
|
|
335
|
+
}),
|
|
336
|
+
};
|
|
337
|
+
};
|