@convex-dev/better-auth 0.9.11 → 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 -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 +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 -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/src/client/index.ts
CHANGED
|
@@ -1,778 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type DataModelFromSchemaDefinition,
|
|
3
|
-
type DefaultFunctionArgs,
|
|
4
|
-
type FunctionHandle,
|
|
5
|
-
type FunctionReference,
|
|
6
|
-
type GenericActionCtx,
|
|
7
|
-
type GenericDataModel,
|
|
8
|
-
type GenericMutationCtx,
|
|
9
|
-
type GenericQueryCtx,
|
|
10
|
-
type GenericSchema,
|
|
11
|
-
type HttpRouter,
|
|
12
|
-
type SchemaDefinition,
|
|
13
|
-
httpActionGeneric,
|
|
14
|
-
internalMutationGeneric,
|
|
15
|
-
mutationGeneric,
|
|
16
|
-
paginationOptsValidator,
|
|
17
|
-
queryGeneric,
|
|
18
|
-
} from "convex/server";
|
|
19
|
-
import { type GenericId, type Infer, v } from "convex/values";
|
|
20
1
|
import { convexAdapter } from "./adapter.js";
|
|
21
|
-
import { type Auth, betterAuth } from "better-auth";
|
|
22
|
-
import type { DBAdapterInstance } from "better-auth/adapters";
|
|
23
|
-
import { asyncMap } from "convex-helpers";
|
|
24
|
-
import { partial } from "convex-helpers/validators";
|
|
25
|
-
import {
|
|
26
|
-
adapterWhereValidator,
|
|
27
|
-
checkUniqueFields,
|
|
28
|
-
hasUniqueFields,
|
|
29
|
-
listOne,
|
|
30
|
-
paginate,
|
|
31
|
-
selectFields,
|
|
32
|
-
} from "./adapterUtils.js";
|
|
33
|
-
import { corsRouter } from "convex-helpers/server/cors";
|
|
34
2
|
import { version as convexVersion } from "convex";
|
|
35
|
-
import
|
|
36
|
-
import
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
export type CreateAuth<
|
|
48
|
-
DataModel extends GenericDataModel,
|
|
49
|
-
A extends ReturnType<typeof betterAuth> = Auth,
|
|
50
|
-
> =
|
|
51
|
-
| ((ctx: GenericCtx<DataModel>) => A)
|
|
52
|
-
| ((ctx: GenericCtx<DataModel>, opts?: { optionsOnly?: boolean }) => A);
|
|
53
|
-
|
|
54
|
-
export const getStaticAuth = <
|
|
55
|
-
DataModel extends GenericDataModel,
|
|
56
|
-
Auth extends ReturnType<typeof betterAuth>,
|
|
57
|
-
>(
|
|
58
|
-
createAuth: CreateAuth<DataModel, Auth>
|
|
59
|
-
): Auth => {
|
|
60
|
-
return createAuth({} as any, { optionsOnly: true });
|
|
61
|
-
};
|
|
3
|
+
import semverLt from "semver/functions/lt.js";
|
|
4
|
+
import {
|
|
5
|
+
type AuthFunctions,
|
|
6
|
+
createClient,
|
|
7
|
+
type Triggers,
|
|
8
|
+
} from "./create-client.js";
|
|
9
|
+
import { createApi } from "./create-api.js";
|
|
10
|
+
import {
|
|
11
|
+
type CreateAuth,
|
|
12
|
+
type EventFunction,
|
|
13
|
+
type GenericCtx,
|
|
14
|
+
} from "../utils/index.js";
|
|
62
15
|
|
|
63
|
-
if (
|
|
16
|
+
if (semverLt(convexVersion, "1.25.0")) {
|
|
64
17
|
throw new Error("Convex version must be at least 1.25.0");
|
|
65
18
|
}
|
|
66
19
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
v.literal("_id")
|
|
77
|
-
),
|
|
78
|
-
operator: v.optional(
|
|
79
|
-
v.union(
|
|
80
|
-
v.literal("lt"),
|
|
81
|
-
v.literal("lte"),
|
|
82
|
-
v.literal("gt"),
|
|
83
|
-
v.literal("gte"),
|
|
84
|
-
v.literal("eq"),
|
|
85
|
-
v.literal("in"),
|
|
86
|
-
v.literal("not_in"),
|
|
87
|
-
v.literal("ne"),
|
|
88
|
-
v.literal("contains"),
|
|
89
|
-
v.literal("starts_with"),
|
|
90
|
-
v.literal("ends_with")
|
|
91
|
-
)
|
|
92
|
-
),
|
|
93
|
-
value: v.union(
|
|
94
|
-
v.string(),
|
|
95
|
-
v.number(),
|
|
96
|
-
v.boolean(),
|
|
97
|
-
v.array(v.string()),
|
|
98
|
-
v.array(v.number()),
|
|
99
|
-
v.null()
|
|
100
|
-
),
|
|
101
|
-
connector: v.optional(v.union(v.literal("AND"), v.literal("OR"))),
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
export type EventFunction<T extends DefaultFunctionArgs> = FunctionReference<
|
|
105
|
-
"mutation",
|
|
106
|
-
"internal" | "public",
|
|
107
|
-
T
|
|
108
|
-
>;
|
|
109
|
-
|
|
110
|
-
export type GenericCtx<DataModel extends GenericDataModel = GenericDataModel> =
|
|
111
|
-
| GenericQueryCtx<DataModel>
|
|
112
|
-
| GenericMutationCtx<DataModel>
|
|
113
|
-
| GenericActionCtx<DataModel>;
|
|
114
|
-
|
|
115
|
-
export type AuthFunctions = {
|
|
116
|
-
onCreate?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
|
|
117
|
-
onUpdate?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
|
|
118
|
-
onDelete?: FunctionReference<"mutation", "internal", { [key: string]: any }>;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
export const createApi = <
|
|
122
|
-
DataModel extends GenericDataModel,
|
|
123
|
-
Schema extends SchemaDefinition<any, any>,
|
|
124
|
-
>(
|
|
125
|
-
schema: Schema,
|
|
126
|
-
createAuth: CreateAuth<DataModel>
|
|
127
|
-
) => {
|
|
128
|
-
const betterAuthSchema = getAuthTables(getStaticAuth(createAuth).options);
|
|
129
|
-
return {
|
|
130
|
-
migrationRemoveUserId: mutationGeneric({
|
|
131
|
-
args: {
|
|
132
|
-
userId: v.string(),
|
|
133
|
-
},
|
|
134
|
-
handler: async (ctx, args) => {
|
|
135
|
-
await ctx.db.patch(args.userId as GenericId<"user">, {
|
|
136
|
-
userId: undefined,
|
|
137
|
-
});
|
|
138
|
-
},
|
|
139
|
-
}),
|
|
140
|
-
create: mutationGeneric({
|
|
141
|
-
args: {
|
|
142
|
-
input: v.union(
|
|
143
|
-
...Object.entries(schema.tables).map(([model, table]) =>
|
|
144
|
-
v.object({
|
|
145
|
-
model: v.literal(model),
|
|
146
|
-
data: v.object((table as any).validator.fields),
|
|
147
|
-
})
|
|
148
|
-
)
|
|
149
|
-
),
|
|
150
|
-
select: v.optional(v.array(v.string())),
|
|
151
|
-
onCreateHandle: v.optional(v.string()),
|
|
152
|
-
},
|
|
153
|
-
handler: async (ctx, args) => {
|
|
154
|
-
await checkUniqueFields(
|
|
155
|
-
ctx,
|
|
156
|
-
schema,
|
|
157
|
-
betterAuthSchema,
|
|
158
|
-
args.input.model,
|
|
159
|
-
args.input.data
|
|
160
|
-
);
|
|
161
|
-
const id = await ctx.db.insert(
|
|
162
|
-
args.input.model as any,
|
|
163
|
-
args.input.data
|
|
164
|
-
);
|
|
165
|
-
const doc = await ctx.db.get(id);
|
|
166
|
-
if (!doc) {
|
|
167
|
-
throw new Error(`Failed to create ${args.input.model}`);
|
|
168
|
-
}
|
|
169
|
-
const result = selectFields(doc, args.select);
|
|
170
|
-
if (args.onCreateHandle) {
|
|
171
|
-
await ctx.runMutation(
|
|
172
|
-
args.onCreateHandle as FunctionHandle<"mutation">,
|
|
173
|
-
{
|
|
174
|
-
model: args.input.model,
|
|
175
|
-
doc,
|
|
176
|
-
}
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
return result;
|
|
180
|
-
},
|
|
181
|
-
}),
|
|
182
|
-
findOne: queryGeneric({
|
|
183
|
-
args: {
|
|
184
|
-
model: v.union(
|
|
185
|
-
...Object.keys(schema.tables).map((model) => v.literal(model))
|
|
186
|
-
),
|
|
187
|
-
where: v.optional(v.array(adapterWhereValidator)),
|
|
188
|
-
select: v.optional(v.array(v.string())),
|
|
189
|
-
},
|
|
190
|
-
handler: async (ctx, args) => {
|
|
191
|
-
return await listOne(ctx, schema, betterAuthSchema, args);
|
|
192
|
-
},
|
|
193
|
-
}),
|
|
194
|
-
findMany: queryGeneric({
|
|
195
|
-
args: {
|
|
196
|
-
model: v.union(
|
|
197
|
-
...Object.keys(schema.tables).map((model) => v.literal(model))
|
|
198
|
-
),
|
|
199
|
-
where: v.optional(v.array(adapterWhereValidator)),
|
|
200
|
-
limit: v.optional(v.number()),
|
|
201
|
-
sortBy: v.optional(
|
|
202
|
-
v.object({
|
|
203
|
-
direction: v.union(v.literal("asc"), v.literal("desc")),
|
|
204
|
-
field: v.string(),
|
|
205
|
-
})
|
|
206
|
-
),
|
|
207
|
-
offset: v.optional(v.number()),
|
|
208
|
-
paginationOpts: paginationOptsValidator,
|
|
209
|
-
},
|
|
210
|
-
handler: async (ctx, args) => {
|
|
211
|
-
return await paginate(ctx, schema, betterAuthSchema, args);
|
|
212
|
-
},
|
|
213
|
-
}),
|
|
214
|
-
updateOne: mutationGeneric({
|
|
215
|
-
args: {
|
|
216
|
-
input: v.union(
|
|
217
|
-
...Object.entries(schema.tables).map(
|
|
218
|
-
([name, table]: [string, Schema["tables"][string]]) => {
|
|
219
|
-
const tableName = name as TableNames;
|
|
220
|
-
const fields = partial(table.validator.fields);
|
|
221
|
-
return v.object({
|
|
222
|
-
model: v.literal(tableName),
|
|
223
|
-
update: v.object(fields),
|
|
224
|
-
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
)
|
|
228
|
-
),
|
|
229
|
-
onUpdateHandle: v.optional(v.string()),
|
|
230
|
-
},
|
|
231
|
-
handler: async (ctx, args) => {
|
|
232
|
-
const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
|
|
233
|
-
if (!doc) {
|
|
234
|
-
throw new Error(`Failed to update ${args.input.model}`);
|
|
235
|
-
}
|
|
236
|
-
await checkUniqueFields(
|
|
237
|
-
ctx,
|
|
238
|
-
schema,
|
|
239
|
-
betterAuthSchema,
|
|
240
|
-
args.input.model,
|
|
241
|
-
args.input.update,
|
|
242
|
-
doc
|
|
243
|
-
);
|
|
244
|
-
await ctx.db.patch(
|
|
245
|
-
doc._id as GenericId<string>,
|
|
246
|
-
args.input.update as any
|
|
247
|
-
);
|
|
248
|
-
const updatedDoc = await ctx.db.get(doc._id as GenericId<string>);
|
|
249
|
-
if (!updatedDoc) {
|
|
250
|
-
throw new Error(`Failed to update ${args.input.model}`);
|
|
251
|
-
}
|
|
252
|
-
if (args.onUpdateHandle) {
|
|
253
|
-
await ctx.runMutation(
|
|
254
|
-
args.onUpdateHandle as FunctionHandle<"mutation">,
|
|
255
|
-
{
|
|
256
|
-
model: args.input.model,
|
|
257
|
-
newDoc: updatedDoc,
|
|
258
|
-
oldDoc: doc,
|
|
259
|
-
}
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
return updatedDoc;
|
|
263
|
-
},
|
|
264
|
-
}),
|
|
265
|
-
updateMany: mutationGeneric({
|
|
266
|
-
args: {
|
|
267
|
-
input: v.union(
|
|
268
|
-
...Object.entries(schema.tables).map(
|
|
269
|
-
([name, table]: [string, Schema["tables"][string]]) => {
|
|
270
|
-
const tableName = name as TableNames;
|
|
271
|
-
const fields = partial(table.validator.fields);
|
|
272
|
-
return v.object({
|
|
273
|
-
model: v.literal(tableName),
|
|
274
|
-
update: v.object(fields),
|
|
275
|
-
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
)
|
|
279
|
-
),
|
|
280
|
-
paginationOpts: paginationOptsValidator,
|
|
281
|
-
onUpdateHandle: v.optional(v.string()),
|
|
282
|
-
},
|
|
283
|
-
handler: async (ctx, args) => {
|
|
284
|
-
const { page, ...result } = await paginate(
|
|
285
|
-
ctx,
|
|
286
|
-
schema,
|
|
287
|
-
betterAuthSchema,
|
|
288
|
-
{
|
|
289
|
-
...args.input,
|
|
290
|
-
paginationOpts: args.paginationOpts,
|
|
291
|
-
}
|
|
292
|
-
);
|
|
293
|
-
if (args.input.update) {
|
|
294
|
-
if (
|
|
295
|
-
hasUniqueFields(
|
|
296
|
-
betterAuthSchema,
|
|
297
|
-
args.input.model,
|
|
298
|
-
args.input.update ?? {}
|
|
299
|
-
) &&
|
|
300
|
-
page.length > 1
|
|
301
|
-
) {
|
|
302
|
-
throw new Error(
|
|
303
|
-
`Attempted to set unique fields in multiple documents in ${args.input.model} with the same value. Fields: ${Object.keys(args.input.update ?? {}).join(", ")}`
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
await asyncMap(page, async (doc) => {
|
|
307
|
-
await checkUniqueFields(
|
|
308
|
-
ctx,
|
|
309
|
-
schema,
|
|
310
|
-
betterAuthSchema,
|
|
311
|
-
args.input.model,
|
|
312
|
-
args.input.update ?? {},
|
|
313
|
-
doc
|
|
314
|
-
);
|
|
315
|
-
await ctx.db.patch(
|
|
316
|
-
doc._id as GenericId<string>,
|
|
317
|
-
args.input.update as any
|
|
318
|
-
);
|
|
319
|
-
|
|
320
|
-
if (args.onUpdateHandle) {
|
|
321
|
-
await ctx.runMutation(
|
|
322
|
-
args.onUpdateHandle as FunctionHandle<"mutation">,
|
|
323
|
-
{
|
|
324
|
-
model: args.input.model,
|
|
325
|
-
newDoc: await ctx.db.get(doc._id as GenericId<string>),
|
|
326
|
-
oldDoc: doc,
|
|
327
|
-
}
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
return {
|
|
333
|
-
...result,
|
|
334
|
-
count: page.length,
|
|
335
|
-
ids: page.map((doc) => doc._id),
|
|
336
|
-
};
|
|
337
|
-
},
|
|
338
|
-
}),
|
|
339
|
-
deleteOne: mutationGeneric({
|
|
340
|
-
args: {
|
|
341
|
-
input: v.union(
|
|
342
|
-
...Object.keys(schema.tables).map((name: string) => {
|
|
343
|
-
const tableName = name as TableNames;
|
|
344
|
-
return v.object({
|
|
345
|
-
model: v.literal(tableName),
|
|
346
|
-
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
347
|
-
});
|
|
348
|
-
})
|
|
349
|
-
),
|
|
350
|
-
onDeleteHandle: v.optional(v.string()),
|
|
351
|
-
},
|
|
352
|
-
handler: async (ctx, args) => {
|
|
353
|
-
const doc = await listOne(ctx, schema, betterAuthSchema, args.input);
|
|
354
|
-
if (!doc) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
await ctx.db.delete(doc._id as GenericId<string>);
|
|
358
|
-
if (args.onDeleteHandle) {
|
|
359
|
-
await ctx.runMutation(
|
|
360
|
-
args.onDeleteHandle as FunctionHandle<"mutation">,
|
|
361
|
-
{ model: args.input.model, doc }
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
return doc;
|
|
365
|
-
},
|
|
366
|
-
}),
|
|
367
|
-
deleteMany: mutationGeneric({
|
|
368
|
-
args: {
|
|
369
|
-
input: v.union(
|
|
370
|
-
...Object.keys(schema.tables).map((name: string) => {
|
|
371
|
-
const tableName = name as TableNames;
|
|
372
|
-
return v.object({
|
|
373
|
-
model: v.literal(tableName),
|
|
374
|
-
where: v.optional(v.array(whereValidator(schema, tableName))),
|
|
375
|
-
});
|
|
376
|
-
})
|
|
377
|
-
),
|
|
378
|
-
paginationOpts: paginationOptsValidator,
|
|
379
|
-
onDeleteHandle: v.optional(v.string()),
|
|
380
|
-
},
|
|
381
|
-
handler: async (ctx, args) => {
|
|
382
|
-
const { page, ...result } = await paginate(
|
|
383
|
-
ctx,
|
|
384
|
-
schema,
|
|
385
|
-
betterAuthSchema,
|
|
386
|
-
{
|
|
387
|
-
...args.input,
|
|
388
|
-
paginationOpts: args.paginationOpts,
|
|
389
|
-
}
|
|
390
|
-
);
|
|
391
|
-
await asyncMap(page, async (doc) => {
|
|
392
|
-
if (args.onDeleteHandle) {
|
|
393
|
-
await ctx.runMutation(
|
|
394
|
-
args.onDeleteHandle as FunctionHandle<"mutation">,
|
|
395
|
-
{
|
|
396
|
-
model: args.input.model,
|
|
397
|
-
doc,
|
|
398
|
-
}
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
await ctx.db.delete(doc._id as GenericId<string>);
|
|
402
|
-
});
|
|
403
|
-
return {
|
|
404
|
-
...result,
|
|
405
|
-
count: page.length,
|
|
406
|
-
ids: page.map((doc) => doc._id),
|
|
407
|
-
};
|
|
408
|
-
},
|
|
409
|
-
}),
|
|
410
|
-
};
|
|
411
|
-
};
|
|
412
|
-
|
|
413
|
-
export type Triggers<
|
|
414
|
-
DataModel extends GenericDataModel,
|
|
415
|
-
Schema extends SchemaDefinition<any, any>,
|
|
416
|
-
> = {
|
|
417
|
-
[K in keyof Schema["tables"]]?: {
|
|
418
|
-
onCreate?: <Ctx extends GenericMutationCtx<DataModel>>(
|
|
419
|
-
ctx: Ctx,
|
|
420
|
-
doc: Infer<Schema["tables"][K]["validator"]> & {
|
|
421
|
-
_id: string;
|
|
422
|
-
_creationTime: number;
|
|
423
|
-
}
|
|
424
|
-
) => Promise<void>;
|
|
425
|
-
onUpdate?: <Ctx extends GenericMutationCtx<DataModel>>(
|
|
426
|
-
ctx: Ctx,
|
|
427
|
-
newDoc: Infer<Schema["tables"][K]["validator"]> & {
|
|
428
|
-
_id: string;
|
|
429
|
-
_creationTime: number;
|
|
430
|
-
},
|
|
431
|
-
oldDoc: Infer<Schema["tables"][K]["validator"]> & {
|
|
432
|
-
_id: string;
|
|
433
|
-
_creationTime: number;
|
|
434
|
-
}
|
|
435
|
-
) => Promise<void>;
|
|
436
|
-
onDelete?: <Ctx extends GenericMutationCtx<DataModel>>(
|
|
437
|
-
ctx: Ctx,
|
|
438
|
-
doc: Infer<Schema["tables"][K]["validator"]> & {
|
|
439
|
-
_id: string;
|
|
440
|
-
_creationTime: number;
|
|
441
|
-
}
|
|
442
|
-
) => Promise<void>;
|
|
443
|
-
};
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
type SlimComponentApi = {
|
|
447
|
-
adapter: {
|
|
448
|
-
create: FunctionReference<"mutation", "internal">;
|
|
449
|
-
findOne: FunctionReference<"query", "internal">;
|
|
450
|
-
findMany: FunctionReference<"query", "internal">;
|
|
451
|
-
updateOne: FunctionReference<"mutation", "internal">;
|
|
452
|
-
updateMany: FunctionReference<"mutation", "internal">;
|
|
453
|
-
deleteOne: FunctionReference<"mutation", "internal">;
|
|
454
|
-
deleteMany: FunctionReference<"mutation", "internal">;
|
|
455
|
-
migrationRemoveUserId?: FunctionReference<"mutation", "internal">;
|
|
456
|
-
};
|
|
457
|
-
adapterTest?: ComponentApi["adapterTest"];
|
|
458
|
-
};
|
|
459
|
-
|
|
460
|
-
export const createClient = <
|
|
461
|
-
DataModel extends GenericDataModel,
|
|
462
|
-
Schema extends SchemaDefinition<GenericSchema, true> = typeof defaultSchema,
|
|
463
|
-
Api extends SlimComponentApi = SlimComponentApi,
|
|
464
|
-
>(
|
|
465
|
-
component: Api,
|
|
466
|
-
config?: {
|
|
467
|
-
local?: {
|
|
468
|
-
schema?: Schema;
|
|
469
|
-
};
|
|
470
|
-
verbose?: boolean;
|
|
471
|
-
} & (
|
|
472
|
-
| {
|
|
473
|
-
triggers: Triggers<DataModel, Schema>;
|
|
474
|
-
authFunctions: AuthFunctions;
|
|
475
|
-
}
|
|
476
|
-
| { triggers?: undefined }
|
|
477
|
-
)
|
|
478
|
-
) => {
|
|
479
|
-
type BetterAuthDataModel = DataModelFromSchemaDefinition<Schema>;
|
|
480
|
-
|
|
481
|
-
const safeGetAuthUser = async (ctx: GenericCtx<DataModel>) => {
|
|
482
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
483
|
-
if (!identity) {
|
|
484
|
-
return;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const doc = (await ctx.runQuery(component.adapter.findOne, {
|
|
488
|
-
model: "user",
|
|
489
|
-
where: [
|
|
490
|
-
{
|
|
491
|
-
field: "_id",
|
|
492
|
-
value: identity.subject,
|
|
493
|
-
},
|
|
494
|
-
],
|
|
495
|
-
})) as BetterAuthDataModel["user"]["document"] | null;
|
|
496
|
-
if (!doc) {
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
return doc;
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
const getHeaders = async (ctx: GenericCtx<DataModel>) => {
|
|
503
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
504
|
-
if (!identity) {
|
|
505
|
-
return new Headers();
|
|
506
|
-
}
|
|
507
|
-
const session = await ctx.runQuery(component.adapter.findOne, {
|
|
508
|
-
model: "session",
|
|
509
|
-
where: [
|
|
510
|
-
{
|
|
511
|
-
field: "_id",
|
|
512
|
-
value: identity.sessionId as string,
|
|
513
|
-
},
|
|
514
|
-
],
|
|
515
|
-
});
|
|
516
|
-
return new Headers({
|
|
517
|
-
...(session?.token ? { authorization: `Bearer ${session.token}` } : {}),
|
|
518
|
-
...(session?.ipAddress
|
|
519
|
-
? { "x-forwarded-for": session.ipAddress as string }
|
|
520
|
-
: {}),
|
|
521
|
-
});
|
|
522
|
-
};
|
|
523
|
-
|
|
524
|
-
return {
|
|
525
|
-
component,
|
|
526
|
-
adapter: (ctx: GenericCtx<DataModel>) =>
|
|
527
|
-
convexAdapter<DataModel, typeof ctx, Schema>(ctx, component, {
|
|
528
|
-
...config,
|
|
529
|
-
debugLogs: config?.verbose,
|
|
530
|
-
}),
|
|
531
|
-
|
|
532
|
-
getAuth: async <T extends CreateAuth<DataModel>>(
|
|
533
|
-
createAuth: T,
|
|
534
|
-
ctx: GenericCtx<DataModel>
|
|
535
|
-
) => ({
|
|
536
|
-
auth: createAuth(ctx) as ReturnType<T>,
|
|
537
|
-
headers: await getHeaders(ctx),
|
|
538
|
-
}),
|
|
539
|
-
|
|
540
|
-
getHeaders,
|
|
541
|
-
|
|
542
|
-
/**
|
|
543
|
-
* Returns the current user or null if the user is not found
|
|
544
|
-
* @param ctx - The Convex context
|
|
545
|
-
* @returns The user or null if the user is not found
|
|
546
|
-
*/
|
|
547
|
-
safeGetAuthUser,
|
|
548
|
-
|
|
549
|
-
/**
|
|
550
|
-
* Returns the current user.
|
|
551
|
-
* @param ctx - The Convex context
|
|
552
|
-
* @returns The user or throws an error if the user is not found
|
|
553
|
-
*/
|
|
554
|
-
getAuthUser: async (ctx: GenericCtx<DataModel>) => {
|
|
555
|
-
const user = await safeGetAuthUser(ctx);
|
|
556
|
-
if (!user) {
|
|
557
|
-
throw new Error("Unauthenticated");
|
|
558
|
-
}
|
|
559
|
-
return user;
|
|
560
|
-
},
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* Returns a user by their Better Auth user id.
|
|
564
|
-
* @param ctx - The Convex context
|
|
565
|
-
* @param id - The Better Auth user id
|
|
566
|
-
* @returns The user or null if the user is not found
|
|
567
|
-
*/
|
|
568
|
-
getAnyUserById: async (ctx: GenericCtx<DataModel>, id: string) => {
|
|
569
|
-
return (await ctx.runQuery(component.adapter.findOne, {
|
|
570
|
-
model: "user",
|
|
571
|
-
where: [{ field: "_id", value: id }],
|
|
572
|
-
})) as BetterAuthDataModel["user"]["document"] | null;
|
|
573
|
-
},
|
|
574
|
-
|
|
575
|
-
// Replaces 0.7 behavior of returning a new user id from
|
|
576
|
-
// onCreateUser, deprecated in 0.9
|
|
577
|
-
/**
|
|
578
|
-
* Replaces 0.7 behavior of returning a new user id from
|
|
579
|
-
* onCreateUser
|
|
580
|
-
* @param ctx - The Convex context
|
|
581
|
-
* @param authId - The Better Auth user id
|
|
582
|
-
* @param userId - The app user id
|
|
583
|
-
* @deprecated in 0.9
|
|
584
|
-
*/
|
|
585
|
-
setUserId: async (
|
|
586
|
-
ctx: GenericMutationCtx<DataModel>,
|
|
587
|
-
authId: string,
|
|
588
|
-
userId: string
|
|
589
|
-
) => {
|
|
590
|
-
await ctx.runMutation(component.adapter.updateOne, {
|
|
591
|
-
input: {
|
|
592
|
-
model: "user",
|
|
593
|
-
where: [{ field: "_id", value: authId }],
|
|
594
|
-
update: { userId },
|
|
595
|
-
},
|
|
596
|
-
});
|
|
597
|
-
},
|
|
598
|
-
|
|
599
|
-
/**
|
|
600
|
-
* Temporary method to simplify 0.9 migration, gets a user by `userId` field
|
|
601
|
-
* @param ctx - The Convex context
|
|
602
|
-
* @param userId - The app user id
|
|
603
|
-
* @returns The user or null if the user is not found
|
|
604
|
-
*/
|
|
605
|
-
migrationGetUser: async (
|
|
606
|
-
ctx: GenericMutationCtx<DataModel>,
|
|
607
|
-
userId: string
|
|
608
|
-
) => {
|
|
609
|
-
return (await ctx.runQuery(component.adapter.findOne, {
|
|
610
|
-
model: "user",
|
|
611
|
-
where: [{ field: "userId", value: userId }],
|
|
612
|
-
})) as BetterAuthDataModel["user"]["document"] | null;
|
|
613
|
-
},
|
|
614
|
-
|
|
615
|
-
/**
|
|
616
|
-
* Temporary method to simplify 0.9 migration, removes the `userId` field
|
|
617
|
-
* from the Better Auth user record
|
|
618
|
-
* @param ctx - The Convex context
|
|
619
|
-
* @param userId - The app user id
|
|
620
|
-
*/
|
|
621
|
-
migrationRemoveUserId: async (
|
|
622
|
-
ctx: GenericMutationCtx<DataModel>,
|
|
623
|
-
userId: string
|
|
624
|
-
) => {
|
|
625
|
-
if (!component.adapter.migrationRemoveUserId) {
|
|
626
|
-
throw new Error("migrationRemoveUserId not found");
|
|
627
|
-
}
|
|
628
|
-
await ctx.runMutation(component.adapter.migrationRemoveUserId, {
|
|
629
|
-
userId,
|
|
630
|
-
});
|
|
631
|
-
},
|
|
632
|
-
|
|
633
|
-
triggersApi: () => ({
|
|
634
|
-
onCreate: internalMutationGeneric({
|
|
635
|
-
args: {
|
|
636
|
-
doc: v.any(),
|
|
637
|
-
model: v.string(),
|
|
638
|
-
},
|
|
639
|
-
handler: async (ctx, args) => {
|
|
640
|
-
await config?.triggers?.[args.model]?.onCreate?.(ctx, args.doc);
|
|
641
|
-
},
|
|
642
|
-
}),
|
|
643
|
-
onUpdate: internalMutationGeneric({
|
|
644
|
-
args: {
|
|
645
|
-
oldDoc: v.any(),
|
|
646
|
-
newDoc: v.any(),
|
|
647
|
-
model: v.string(),
|
|
648
|
-
},
|
|
649
|
-
handler: async (ctx, args) => {
|
|
650
|
-
await config?.triggers?.[args.model]?.onUpdate?.(
|
|
651
|
-
ctx,
|
|
652
|
-
args.newDoc,
|
|
653
|
-
args.oldDoc
|
|
654
|
-
);
|
|
655
|
-
},
|
|
656
|
-
}),
|
|
657
|
-
onDelete: internalMutationGeneric({
|
|
658
|
-
args: {
|
|
659
|
-
doc: v.any(),
|
|
660
|
-
model: v.string(),
|
|
661
|
-
},
|
|
662
|
-
handler: async (ctx, args) => {
|
|
663
|
-
await config?.triggers?.[args.model]?.onDelete?.(ctx, args.doc);
|
|
664
|
-
},
|
|
665
|
-
}),
|
|
666
|
-
}),
|
|
667
|
-
|
|
668
|
-
registerRoutes: (
|
|
669
|
-
http: HttpRouter,
|
|
670
|
-
createAuth: CreateAuth<DataModel>,
|
|
671
|
-
opts: {
|
|
672
|
-
cors?:
|
|
673
|
-
| boolean
|
|
674
|
-
| {
|
|
675
|
-
// These values are appended to the default values
|
|
676
|
-
allowedOrigins?: string[];
|
|
677
|
-
allowedHeaders?: string[];
|
|
678
|
-
exposedHeaders?: string[];
|
|
679
|
-
};
|
|
680
|
-
} = {}
|
|
681
|
-
) => {
|
|
682
|
-
const staticAuth = getStaticAuth(createAuth);
|
|
683
|
-
const path = staticAuth.options.basePath ?? "/api/auth";
|
|
684
|
-
const authRequestHandler = httpActionGeneric(async (ctx, request) => {
|
|
685
|
-
if (config?.verbose) {
|
|
686
|
-
console.log("options.baseURL", staticAuth.options.baseURL);
|
|
687
|
-
console.log("request headers", request.headers);
|
|
688
|
-
}
|
|
689
|
-
const auth = createAuth(ctx as any);
|
|
690
|
-
const response = await auth.handler(request);
|
|
691
|
-
if (config?.verbose) {
|
|
692
|
-
console.log("response headers", response.headers);
|
|
693
|
-
}
|
|
694
|
-
return response;
|
|
695
|
-
});
|
|
696
|
-
const wellKnown = http.lookup("/.well-known/openid-configuration", "GET");
|
|
697
|
-
|
|
698
|
-
// If registerRoutes is used multiple times, this may already be defined
|
|
699
|
-
if (!wellKnown) {
|
|
700
|
-
// Redirect root well-known to api well-known
|
|
701
|
-
http.route({
|
|
702
|
-
path: "/.well-known/openid-configuration",
|
|
703
|
-
method: "GET",
|
|
704
|
-
handler: httpActionGeneric(async () => {
|
|
705
|
-
const url = `${process.env.CONVEX_SITE_URL}${path}/convex/.well-known/openid-configuration`;
|
|
706
|
-
return Response.redirect(url);
|
|
707
|
-
}),
|
|
708
|
-
});
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
if (!opts.cors) {
|
|
712
|
-
http.route({
|
|
713
|
-
pathPrefix: `${path}/`,
|
|
714
|
-
method: "GET",
|
|
715
|
-
handler: authRequestHandler,
|
|
716
|
-
});
|
|
717
|
-
|
|
718
|
-
http.route({
|
|
719
|
-
pathPrefix: `${path}/`,
|
|
720
|
-
method: "POST",
|
|
721
|
-
handler: authRequestHandler,
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
return;
|
|
725
|
-
}
|
|
726
|
-
const corsOpts =
|
|
727
|
-
typeof opts.cors === "boolean"
|
|
728
|
-
? { allowedOrigins: [], allowedHeaders: [], exposedHeaders: [] }
|
|
729
|
-
: opts.cors;
|
|
730
|
-
let trustedOriginsOption:
|
|
731
|
-
| string[]
|
|
732
|
-
| ((request: Request) => string[] | Promise<string[]>)
|
|
733
|
-
| undefined;
|
|
734
|
-
const cors = corsRouter(http, {
|
|
735
|
-
allowedOrigins: async (request) => {
|
|
736
|
-
trustedOriginsOption =
|
|
737
|
-
trustedOriginsOption ??
|
|
738
|
-
(await staticAuth.$context).options.trustedOrigins ??
|
|
739
|
-
[];
|
|
740
|
-
const trustedOrigins = Array.isArray(trustedOriginsOption)
|
|
741
|
-
? trustedOriginsOption
|
|
742
|
-
: await trustedOriginsOption(request);
|
|
743
|
-
return trustedOrigins
|
|
744
|
-
.map((origin) =>
|
|
745
|
-
// Strip trailing wildcards, unsupported for allowedOrigins
|
|
746
|
-
origin.endsWith("*") && origin.length > 1
|
|
747
|
-
? origin.slice(0, -1)
|
|
748
|
-
: origin
|
|
749
|
-
)
|
|
750
|
-
.concat(corsOpts.allowedOrigins ?? []);
|
|
751
|
-
},
|
|
752
|
-
allowCredentials: true,
|
|
753
|
-
allowedHeaders: [
|
|
754
|
-
"Content-Type",
|
|
755
|
-
"Better-Auth-Cookie",
|
|
756
|
-
"Authorization",
|
|
757
|
-
].concat(corsOpts.allowedHeaders ?? []),
|
|
758
|
-
exposedHeaders: ["Set-Better-Auth-Cookie"].concat(
|
|
759
|
-
corsOpts.exposedHeaders ?? []
|
|
760
|
-
),
|
|
761
|
-
debug: config?.verbose,
|
|
762
|
-
enforceAllowOrigins: false,
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
cors.route({
|
|
766
|
-
pathPrefix: `${path}/`,
|
|
767
|
-
method: "GET",
|
|
768
|
-
handler: authRequestHandler,
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
cors.route({
|
|
772
|
-
pathPrefix: `${path}/`,
|
|
773
|
-
method: "POST",
|
|
774
|
-
handler: authRequestHandler,
|
|
775
|
-
});
|
|
776
|
-
},
|
|
777
|
-
};
|
|
20
|
+
export {
|
|
21
|
+
convexAdapter,
|
|
22
|
+
createClient,
|
|
23
|
+
createApi,
|
|
24
|
+
type CreateAuth,
|
|
25
|
+
type EventFunction,
|
|
26
|
+
type GenericCtx,
|
|
27
|
+
type Triggers,
|
|
28
|
+
type AuthFunctions,
|
|
778
29
|
};
|