@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.
- 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 -476
- 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 +19 -12
- 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 -786
- 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.0",
|
|
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,26 @@
|
|
|
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
110
|
"@better-fetch/fetch": "^1.1.18",
|
|
111
|
+
"@better-auth/passkey": "1.4.7",
|
|
103
112
|
"@edge-runtime/vm": "5.0.0",
|
|
104
113
|
"@eslint/eslintrc": "3.3.1",
|
|
105
114
|
"@eslint/js": "9.39.1",
|
|
106
|
-
"@tanstack/react-start": "^1.
|
|
115
|
+
"@tanstack/react-start": "^1.140.1",
|
|
107
116
|
"@types/common-tags": "^1.8.4",
|
|
108
117
|
"@types/node": "20.19.24",
|
|
109
118
|
"@types/react": "18.3.26",
|
|
110
119
|
"@types/react-dom": "18.3.7",
|
|
111
120
|
"@types/semver": "^7.7.0",
|
|
112
|
-
"@vitejs/plugin-react": "5.0.4",
|
|
113
|
-
"concurrently": "^9.2.0",
|
|
114
121
|
"chokidar-cli": "3.0.0",
|
|
115
|
-
"
|
|
122
|
+
"concurrently": "^9.2.0",
|
|
123
|
+
"convex": "^1.30.0",
|
|
116
124
|
"convex-test": "0.0.41",
|
|
117
125
|
"cpy-cli": "6.0.0",
|
|
118
126
|
"eslint": "9.39.1",
|
|
@@ -120,16 +128,15 @@
|
|
|
120
128
|
"eslint-plugin-react-hooks": "5.2.0",
|
|
121
129
|
"eslint-plugin-react-refresh": "0.4.24",
|
|
122
130
|
"globals": "15.14.0",
|
|
123
|
-
"next": "^
|
|
124
|
-
"npm-run-all2": "
|
|
131
|
+
"next": "^16.0.3",
|
|
132
|
+
"npm-run-all2": "8.0.4",
|
|
125
133
|
"pkg-pr-new": "0.0.60",
|
|
126
134
|
"prettier": "3.6.2",
|
|
127
135
|
"react": "18.3.1",
|
|
128
136
|
"react-dom": "18.3.1",
|
|
129
137
|
"typescript": "5.9.3",
|
|
130
138
|
"typescript-eslint": "8.46.4",
|
|
131
|
-
"
|
|
132
|
-
"vitest": "3.2.4"
|
|
139
|
+
"vitest": "^4.0.15"
|
|
133
140
|
},
|
|
134
141
|
"types": "./dist/client/index.d.ts",
|
|
135
142
|
"module": "./dist/client/index.js",
|
|
@@ -140,6 +147,6 @@
|
|
|
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
|
+
};
|