@better-auth/core 1.4.6-beta.2 → 1.4.6-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +31 -30
- package/dist/api/index.d.mts +1 -2
- package/dist/api/index.mjs +3 -2
- package/dist/async_hooks/index.d.mts +1 -1
- package/dist/async_hooks/index.mjs +2 -1
- package/dist/async_hooks-CrTStdt6.mjs +45 -0
- package/dist/context/index.d.mts +2 -3
- package/dist/context/index.mjs +3 -2
- package/dist/{context-DgQ9XGBl.mjs → context-su4uu82y.mjs} +1 -1
- package/dist/db/adapter/index.d.mts +2 -3
- package/dist/db/adapter/index.mjs +955 -1
- package/dist/db/index.d.mts +2 -3
- package/dist/db/index.mjs +2 -1
- package/dist/env/index.d.mts +1 -1
- package/dist/env/index.mjs +1 -1
- package/dist/error/index.mjs +3 -2
- package/dist/{error-BhAKg8LX.mjs → error-CMXuwPsa.mjs} +1 -1
- package/dist/get-tables-BGfrxIVZ.mjs +252 -0
- package/dist/{index-D_XSRX55.d.mts → index-Bp1Bfmzt.d.mts} +307 -32
- package/dist/{index-DgwIISs7.d.mts → index-Da4Ujjef.d.mts} +0 -1
- package/dist/index.d.mts +2 -3
- package/dist/oauth2/index.d.mts +1 -2
- package/dist/oauth2/index.mjs +1 -1
- package/dist/social-providers/index.d.mts +2 -3
- package/dist/social-providers/index.mjs +22 -8
- package/dist/utils/index.d.mts +10 -1
- package/dist/utils/index.mjs +3 -2
- package/dist/utils-BqQC77zO.mjs +43 -0
- package/package.json +8 -6
- package/src/async_hooks/convex.spec.ts +12 -0
- package/src/async_hooks/index.ts +60 -25
- package/src/db/adapter/factory.ts +1368 -0
- package/src/db/adapter/get-default-field-name.ts +59 -0
- package/src/db/adapter/get-default-model-name.ts +51 -0
- package/src/db/adapter/get-field-attributes.ts +62 -0
- package/src/db/adapter/get-field-name.ts +43 -0
- package/src/db/adapter/get-id-field.ts +141 -0
- package/src/db/adapter/get-model-name.ts +36 -0
- package/src/db/adapter/index.ts +12 -0
- package/src/db/adapter/types.ts +161 -0
- package/src/db/adapter/utils.ts +61 -0
- package/src/db/get-tables.ts +276 -0
- package/src/db/index.ts +2 -0
- package/src/db/test/get-tables.test.ts +62 -0
- package/src/env/logger.ts +4 -6
- package/src/oauth2/oauth-provider.ts +1 -1
- package/src/social-providers/google.ts +46 -17
- package/src/types/context.ts +10 -0
- package/src/types/helper.ts +4 -0
- package/src/types/init-options.ts +24 -24
- package/src/utils/id.ts +5 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/json.ts +25 -0
- package/src/utils/string.ts +3 -0
- package/dist/async_hooks-BfRfbd1J.mjs +0 -18
- package/dist/utils-C5EN75oV.mjs +0 -7
- /package/dist/{env-8yWFh7b8.mjs → env-D6s-lvJz.mjs} +0 -0
- /package/dist/{index-X1Fs3IbM.d.mts → index-D4vfN5ui.d.mts} +0 -0
- /package/dist/{oauth2-B2XPHgx5.mjs → oauth2-7k48hhcV.mjs} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { BetterAuthError } from "../../error";
|
|
2
|
+
import type { BetterAuthDBSchema } from "../type";
|
|
3
|
+
import { initGetDefaultModelName } from "./get-default-model-name";
|
|
4
|
+
|
|
5
|
+
export const initGetDefaultFieldName = ({
|
|
6
|
+
schema,
|
|
7
|
+
usePlural,
|
|
8
|
+
}: {
|
|
9
|
+
schema: BetterAuthDBSchema;
|
|
10
|
+
usePlural: boolean | undefined;
|
|
11
|
+
}) => {
|
|
12
|
+
const getDefaultModelName = initGetDefaultModelName({
|
|
13
|
+
schema,
|
|
14
|
+
usePlural,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* This function helps us get the default field name from the schema defined by devs.
|
|
19
|
+
* Often times, the user will be using the `fieldName` which could had been customized by the users.
|
|
20
|
+
* This function helps us get the actual field name useful to match against the schema. (eg: schema[model].fields[field])
|
|
21
|
+
*
|
|
22
|
+
* If it's still unclear what this does:
|
|
23
|
+
*
|
|
24
|
+
* 1. User can define a custom fieldName.
|
|
25
|
+
* 2. When using a custom fieldName, doing something like `schema[model].fields[field]` will not work.
|
|
26
|
+
*/
|
|
27
|
+
const getDefaultFieldName = ({
|
|
28
|
+
field,
|
|
29
|
+
model: unsafeModel,
|
|
30
|
+
}: {
|
|
31
|
+
model: string;
|
|
32
|
+
field: string;
|
|
33
|
+
}) => {
|
|
34
|
+
// Plugin `schema`s can't define their own `id`. Better-auth auto provides `id` to every schema model.
|
|
35
|
+
// Given this, we can't just check if the `field` (that being `id`) is within the schema's fields, since it is never defined.
|
|
36
|
+
// So we check if the `field` is `id` and if so, we return `id` itself. Otherwise, we return the `field` from the schema.
|
|
37
|
+
if (field === "id" || field === "_id") {
|
|
38
|
+
return "id";
|
|
39
|
+
}
|
|
40
|
+
const model = getDefaultModelName(unsafeModel); // Just to make sure the model name is correct.
|
|
41
|
+
|
|
42
|
+
let f = schema[model]?.fields[field];
|
|
43
|
+
if (!f) {
|
|
44
|
+
const result = Object.entries(schema[model]!.fields!).find(
|
|
45
|
+
([_, f]) => f.fieldName === field,
|
|
46
|
+
);
|
|
47
|
+
if (result) {
|
|
48
|
+
f = result[1];
|
|
49
|
+
field = result[0];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
if (!f) {
|
|
53
|
+
throw new BetterAuthError(`Field ${field} not found in model ${model}`);
|
|
54
|
+
}
|
|
55
|
+
return field;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return getDefaultFieldName;
|
|
59
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { BetterAuthError } from "../../error";
|
|
2
|
+
import type { BetterAuthDBSchema } from "../type";
|
|
3
|
+
|
|
4
|
+
export const initGetDefaultModelName = ({
|
|
5
|
+
usePlural,
|
|
6
|
+
schema,
|
|
7
|
+
}: {
|
|
8
|
+
usePlural: boolean | undefined;
|
|
9
|
+
schema: BetterAuthDBSchema;
|
|
10
|
+
}) => {
|
|
11
|
+
/**
|
|
12
|
+
* This function helps us get the default model name from the schema defined by devs.
|
|
13
|
+
* Often times, the user will be using the `modelName` which could had been customized by the users.
|
|
14
|
+
* This function helps us get the actual model name useful to match against the schema. (eg: schema[model])
|
|
15
|
+
*
|
|
16
|
+
* If it's still unclear what this does:
|
|
17
|
+
*
|
|
18
|
+
* 1. User can define a custom modelName.
|
|
19
|
+
* 2. When using a custom modelName, doing something like `schema[model]` will not work.
|
|
20
|
+
* 3. Using this function helps us get the actual model name based on the user's defined custom modelName.
|
|
21
|
+
*/
|
|
22
|
+
const getDefaultModelName = (model: string) => {
|
|
23
|
+
// It's possible this `model` could had applied `usePlural`.
|
|
24
|
+
// Thus we'll try the search but without the trailing `s`.
|
|
25
|
+
if (usePlural && model.charAt(model.length - 1) === "s") {
|
|
26
|
+
let pluralessModel = model.slice(0, -1);
|
|
27
|
+
let m = schema[pluralessModel] ? pluralessModel : undefined;
|
|
28
|
+
if (!m) {
|
|
29
|
+
m = Object.entries(schema).find(
|
|
30
|
+
([_, f]) => f.modelName === pluralessModel,
|
|
31
|
+
)?.[0];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (m) {
|
|
35
|
+
return m;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let m = schema[model] ? model : undefined;
|
|
40
|
+
if (!m) {
|
|
41
|
+
m = Object.entries(schema).find(([_, f]) => f.modelName === model)?.[0];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!m) {
|
|
45
|
+
throw new BetterAuthError(`Model "${model}" not found in schema`);
|
|
46
|
+
}
|
|
47
|
+
return m;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return getDefaultModelName;
|
|
51
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { BetterAuthError } from "../../error";
|
|
2
|
+
import type { BetterAuthOptions } from "../../types";
|
|
3
|
+
import type { BetterAuthDBSchema } from "../type";
|
|
4
|
+
import { initGetDefaultFieldName } from "./get-default-field-name";
|
|
5
|
+
import { initGetDefaultModelName } from "./get-default-model-name";
|
|
6
|
+
import { initGetIdField } from "./get-id-field";
|
|
7
|
+
|
|
8
|
+
export const initGetFieldAttributes = ({
|
|
9
|
+
usePlural,
|
|
10
|
+
schema,
|
|
11
|
+
options,
|
|
12
|
+
customIdGenerator,
|
|
13
|
+
disableIdGeneration,
|
|
14
|
+
}: {
|
|
15
|
+
usePlural?: boolean;
|
|
16
|
+
schema: BetterAuthDBSchema;
|
|
17
|
+
options: BetterAuthOptions;
|
|
18
|
+
disableIdGeneration?: boolean;
|
|
19
|
+
customIdGenerator?: ((props: { model: string }) => string) | undefined;
|
|
20
|
+
}) => {
|
|
21
|
+
const getDefaultModelName = initGetDefaultModelName({
|
|
22
|
+
usePlural,
|
|
23
|
+
schema,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const getDefaultFieldName = initGetDefaultFieldName({
|
|
27
|
+
usePlural,
|
|
28
|
+
schema,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const idField = initGetIdField({
|
|
32
|
+
usePlural,
|
|
33
|
+
schema,
|
|
34
|
+
options,
|
|
35
|
+
customIdGenerator,
|
|
36
|
+
disableIdGeneration,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const getFieldAttributes = ({
|
|
40
|
+
model,
|
|
41
|
+
field,
|
|
42
|
+
}: {
|
|
43
|
+
model: string;
|
|
44
|
+
field: string;
|
|
45
|
+
}) => {
|
|
46
|
+
const defaultModelName = getDefaultModelName(model);
|
|
47
|
+
const defaultFieldName = getDefaultFieldName({
|
|
48
|
+
field: field,
|
|
49
|
+
model: defaultModelName,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const fields = schema[defaultModelName]!.fields;
|
|
53
|
+
fields.id = idField({ customModelName: defaultModelName });
|
|
54
|
+
const fieldAttributes = fields[defaultFieldName];
|
|
55
|
+
if (!fieldAttributes) {
|
|
56
|
+
throw new BetterAuthError(`Field ${field} not found in model ${model}`);
|
|
57
|
+
}
|
|
58
|
+
return fieldAttributes;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return getFieldAttributes;
|
|
62
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { BetterAuthDBSchema } from "../type";
|
|
2
|
+
import { initGetDefaultFieldName } from "./get-default-field-name";
|
|
3
|
+
import { initGetDefaultModelName } from "./get-default-model-name";
|
|
4
|
+
|
|
5
|
+
export const initGetFieldName = ({
|
|
6
|
+
schema,
|
|
7
|
+
usePlural,
|
|
8
|
+
}: {
|
|
9
|
+
schema: BetterAuthDBSchema;
|
|
10
|
+
usePlural: boolean | undefined;
|
|
11
|
+
}) => {
|
|
12
|
+
const getDefaultModelName = initGetDefaultModelName({
|
|
13
|
+
schema,
|
|
14
|
+
usePlural,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const getDefaultFieldName = initGetDefaultFieldName({
|
|
18
|
+
schema,
|
|
19
|
+
usePlural,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get the field name which is expected to be saved in the database based on the user's schema.
|
|
24
|
+
*
|
|
25
|
+
* This function is useful if you need to save the field name to the database.
|
|
26
|
+
*
|
|
27
|
+
* For example, if the user has defined a custom field name for the `user` model, then you can use this function to get the actual field name from the schema.
|
|
28
|
+
*/
|
|
29
|
+
function getFieldName({
|
|
30
|
+
model: modelName,
|
|
31
|
+
field: fieldName,
|
|
32
|
+
}: {
|
|
33
|
+
model: string;
|
|
34
|
+
field: string;
|
|
35
|
+
}) {
|
|
36
|
+
const model = getDefaultModelName(modelName);
|
|
37
|
+
const field = getDefaultFieldName({ model, field: fieldName });
|
|
38
|
+
|
|
39
|
+
return schema[model]?.fields[field]?.fieldName || field;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return getFieldName;
|
|
43
|
+
};
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { logger } from "../../env";
|
|
2
|
+
import type { BetterAuthOptions } from "../../types";
|
|
3
|
+
import { generateId as defaultGenerateId } from "../../utils";
|
|
4
|
+
import type { BetterAuthDBSchema, DBFieldAttribute } from "../type";
|
|
5
|
+
import { initGetDefaultModelName } from "./get-default-model-name";
|
|
6
|
+
|
|
7
|
+
export const initGetIdField = ({
|
|
8
|
+
usePlural,
|
|
9
|
+
schema,
|
|
10
|
+
disableIdGeneration,
|
|
11
|
+
options,
|
|
12
|
+
customIdGenerator,
|
|
13
|
+
supportsUUIDs,
|
|
14
|
+
}: {
|
|
15
|
+
usePlural?: boolean;
|
|
16
|
+
schema: BetterAuthDBSchema;
|
|
17
|
+
options: BetterAuthOptions;
|
|
18
|
+
disableIdGeneration?: boolean;
|
|
19
|
+
customIdGenerator?: ((props: { model: string }) => string) | undefined;
|
|
20
|
+
supportsUUIDs?: boolean;
|
|
21
|
+
}) => {
|
|
22
|
+
const getDefaultModelName = initGetDefaultModelName({
|
|
23
|
+
usePlural: usePlural,
|
|
24
|
+
schema,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const idField = ({
|
|
28
|
+
customModelName,
|
|
29
|
+
forceAllowId,
|
|
30
|
+
}: {
|
|
31
|
+
customModelName?: string;
|
|
32
|
+
forceAllowId?: boolean;
|
|
33
|
+
}) => {
|
|
34
|
+
const useNumberId =
|
|
35
|
+
options.advanced?.database?.useNumberId ||
|
|
36
|
+
options.advanced?.database?.generateId === "serial";
|
|
37
|
+
const useUUIDs = options.advanced?.database?.generateId === "uuid";
|
|
38
|
+
|
|
39
|
+
let shouldGenerateId: boolean = (() => {
|
|
40
|
+
if (disableIdGeneration) {
|
|
41
|
+
return false;
|
|
42
|
+
} else if (useNumberId && !forceAllowId) {
|
|
43
|
+
// if force allow is true, then we should be using their custom provided id.
|
|
44
|
+
return false;
|
|
45
|
+
} else if (useUUIDs) {
|
|
46
|
+
// should only generate UUIDs via JS if the database doesn't support natively generating UUIDs.
|
|
47
|
+
return !supportsUUIDs;
|
|
48
|
+
} else {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
})();
|
|
52
|
+
|
|
53
|
+
const model = getDefaultModelName(customModelName ?? "id");
|
|
54
|
+
return {
|
|
55
|
+
type: useNumberId ? "number" : "string",
|
|
56
|
+
required: shouldGenerateId ? true : false,
|
|
57
|
+
...(shouldGenerateId
|
|
58
|
+
? {
|
|
59
|
+
defaultValue() {
|
|
60
|
+
if (disableIdGeneration) return undefined;
|
|
61
|
+
let generateId = options.advanced?.database?.generateId;
|
|
62
|
+
if (generateId === false || useNumberId) return undefined;
|
|
63
|
+
if (typeof generateId === "function") {
|
|
64
|
+
return generateId({
|
|
65
|
+
model,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
if (customIdGenerator) {
|
|
69
|
+
return customIdGenerator({ model });
|
|
70
|
+
}
|
|
71
|
+
if (generateId === "uuid") {
|
|
72
|
+
return crypto.randomUUID();
|
|
73
|
+
}
|
|
74
|
+
return defaultGenerateId();
|
|
75
|
+
},
|
|
76
|
+
}
|
|
77
|
+
: {}),
|
|
78
|
+
transform: {
|
|
79
|
+
input: (value) => {
|
|
80
|
+
// Uncomment if need to debug id transformation
|
|
81
|
+
// console.log(`transforming id: `, {
|
|
82
|
+
// id: value,
|
|
83
|
+
// ...(useNumberId ? { useNumberId } : {}),
|
|
84
|
+
// ...(useUUIDs ? { useUUIDs } : {}),
|
|
85
|
+
// ...(forceAllowId ? { forceAllowId } : {}),
|
|
86
|
+
// });
|
|
87
|
+
if (!value) return undefined;
|
|
88
|
+
|
|
89
|
+
if (useNumberId) {
|
|
90
|
+
const numberValue = Number(value);
|
|
91
|
+
// if invalid number, fallback to DB generated number id.
|
|
92
|
+
if (isNaN(numberValue)) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
return numberValue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (useUUIDs) {
|
|
99
|
+
// if it's generated by us, then we should return the value as is.
|
|
100
|
+
if (shouldGenerateId && !forceAllowId) return value;
|
|
101
|
+
if (disableIdGeneration) return undefined;
|
|
102
|
+
// if DB will handle UUID generation, then we should return undefined.
|
|
103
|
+
if (supportsUUIDs) return undefined;
|
|
104
|
+
// if forceAllowId is true, it means we should be using the ID provided during the adapter call.
|
|
105
|
+
if (forceAllowId && typeof value === "string") {
|
|
106
|
+
const uuidRegex =
|
|
107
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
108
|
+
if (uuidRegex.test(value)) {
|
|
109
|
+
return value;
|
|
110
|
+
} else {
|
|
111
|
+
const err = new Error();
|
|
112
|
+
const stack = err.stack
|
|
113
|
+
?.split("\n")
|
|
114
|
+
.filter((_, i) => i !== 1)
|
|
115
|
+
.join("\n")
|
|
116
|
+
.replace("Error:", "");
|
|
117
|
+
logger.warn(
|
|
118
|
+
"[Adapter Factory] - Invalid UUID value for field `id` provided when `forceAllowId` is true. Generating a new UUID.",
|
|
119
|
+
stack,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// if the value is not a string, and the database doesn't support generating it's own UUIDs, then we should be generating the UUID.
|
|
124
|
+
if (typeof value !== "string" && !supportsUUIDs) {
|
|
125
|
+
return crypto.randomUUID();
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return value;
|
|
131
|
+
},
|
|
132
|
+
output: (value) => {
|
|
133
|
+
if (!value) return undefined;
|
|
134
|
+
return String(value);
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
} satisfies DBFieldAttribute;
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return idField;
|
|
141
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { BetterAuthDBSchema } from "../type";
|
|
2
|
+
import { initGetDefaultModelName } from "./get-default-model-name";
|
|
3
|
+
|
|
4
|
+
export const initGetModelName = ({
|
|
5
|
+
usePlural,
|
|
6
|
+
schema,
|
|
7
|
+
}: {
|
|
8
|
+
usePlural: boolean | undefined;
|
|
9
|
+
schema: BetterAuthDBSchema;
|
|
10
|
+
}) => {
|
|
11
|
+
const getDefaultModelName = initGetDefaultModelName({
|
|
12
|
+
schema,
|
|
13
|
+
usePlural,
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Users can overwrite the default model of some tables. This function helps find the correct model name.
|
|
17
|
+
* Furthermore, if the user passes `usePlural` as true in their adapter config,
|
|
18
|
+
* then we should return the model name ending with an `s`.
|
|
19
|
+
*/
|
|
20
|
+
const getModelName = (model: string) => {
|
|
21
|
+
const defaultModelKey = getDefaultModelName(model);
|
|
22
|
+
const useCustomModelName =
|
|
23
|
+
schema &&
|
|
24
|
+
schema[defaultModelKey] &&
|
|
25
|
+
schema[defaultModelKey].modelName !== model;
|
|
26
|
+
|
|
27
|
+
if (useCustomModelName) {
|
|
28
|
+
return usePlural
|
|
29
|
+
? `${schema[defaultModelKey]!.modelName}s`
|
|
30
|
+
: schema[defaultModelKey]!.modelName;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return usePlural ? `${model}s` : model;
|
|
34
|
+
};
|
|
35
|
+
return getModelName;
|
|
36
|
+
};
|
package/src/db/adapter/index.ts
CHANGED
|
@@ -112,6 +112,14 @@ export interface DBAdapterFactoryConfig<
|
|
|
112
112
|
* @default true
|
|
113
113
|
*/
|
|
114
114
|
supportsBooleans?: boolean | undefined;
|
|
115
|
+
/**
|
|
116
|
+
* If the database doesn't support arrays, set this to `false`.
|
|
117
|
+
*
|
|
118
|
+
* We will handle the translation between using `array`s, and saving `string`s to the database.
|
|
119
|
+
*
|
|
120
|
+
* @default false
|
|
121
|
+
*/
|
|
122
|
+
supportsArrays?: boolean | undefined;
|
|
115
123
|
/**
|
|
116
124
|
* Execute multiple operations in a transaction.
|
|
117
125
|
*
|
|
@@ -532,3 +540,7 @@ export interface DBAdapterInstance<
|
|
|
532
540
|
> {
|
|
533
541
|
(options: BetterAuthOptions): DBAdapter<Options>;
|
|
534
542
|
}
|
|
543
|
+
|
|
544
|
+
export * from "./factory";
|
|
545
|
+
export * from "./types";
|
|
546
|
+
export * from "./utils";
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { BetterAuthOptions } from "../../types";
|
|
2
|
+
import type { Prettify } from "../../types/helper";
|
|
3
|
+
import type { BetterAuthDBSchema, DBFieldAttribute } from "../type";
|
|
4
|
+
import type {
|
|
5
|
+
DBAdapterSchemaCreation as AdapterSchemaCreation,
|
|
6
|
+
CustomAdapter as CoreCustomAdapter,
|
|
7
|
+
DBAdapterFactoryConfig,
|
|
8
|
+
JoinConfig,
|
|
9
|
+
DBTransactionAdapter as TransactionAdapter,
|
|
10
|
+
Where,
|
|
11
|
+
} from "./index";
|
|
12
|
+
|
|
13
|
+
export type AdapterFactoryOptions = {
|
|
14
|
+
config: AdapterFactoryConfig;
|
|
15
|
+
adapter: AdapterFactoryCustomizeAdapterCreator;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export interface AdapterFactoryConfig
|
|
19
|
+
extends Omit<DBAdapterFactoryConfig<BetterAuthOptions>, "transaction"> {
|
|
20
|
+
/**
|
|
21
|
+
* Execute multiple operations in a transaction.
|
|
22
|
+
*
|
|
23
|
+
* If the database doesn't support transactions, set this to `false` and operations will be executed sequentially.
|
|
24
|
+
*
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
transaction?:
|
|
28
|
+
| (
|
|
29
|
+
| false
|
|
30
|
+
| (<R>(callback: (trx: TransactionAdapter) => Promise<R>) => Promise<R>)
|
|
31
|
+
)
|
|
32
|
+
| undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type AdapterFactoryCustomizeAdapterCreator = (config: {
|
|
36
|
+
options: BetterAuthOptions;
|
|
37
|
+
/**
|
|
38
|
+
* The schema of the user's Better-Auth instance.
|
|
39
|
+
*/
|
|
40
|
+
schema: BetterAuthDBSchema;
|
|
41
|
+
/**
|
|
42
|
+
* The debug log function.
|
|
43
|
+
*
|
|
44
|
+
* If the config has defined `debugLogs` as `false`, no logs will be shown.
|
|
45
|
+
*/
|
|
46
|
+
debugLog: (...args: any[]) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Get the model name which is expected to be saved in the database based on the user's schema.
|
|
49
|
+
*/
|
|
50
|
+
getModelName: (model: string) => string;
|
|
51
|
+
/**
|
|
52
|
+
* Get the field name which is expected to be saved in the database based on the user's schema.
|
|
53
|
+
*/
|
|
54
|
+
getFieldName: ({ model, field }: { model: string; field: string }) => string;
|
|
55
|
+
/**
|
|
56
|
+
* This function helps us get the default model name from the schema defined by devs.
|
|
57
|
+
* Often times, the user will be using the `modelName` which could had been customized by the users.
|
|
58
|
+
* This function helps us get the actual model name useful to match against the schema. (eg: schema[model])
|
|
59
|
+
*
|
|
60
|
+
* If it's still unclear what this does:
|
|
61
|
+
*
|
|
62
|
+
* 1. User can define a custom modelName.
|
|
63
|
+
* 2. When using a custom modelName, doing something like `schema[model]` will not work.
|
|
64
|
+
* 3. Using this function helps us get the actual model name based on the user's defined custom modelName.
|
|
65
|
+
* 4. Thus allowing us to use `schema[model]`.
|
|
66
|
+
*/
|
|
67
|
+
getDefaultModelName: (model: string) => string;
|
|
68
|
+
/**
|
|
69
|
+
* This function helps us get the default field name from the schema defined by devs.
|
|
70
|
+
* Often times, the user will be using the `fieldName` which could had been customized by the users.
|
|
71
|
+
* This function helps us get the actual field name useful to match against the schema. (eg: schema[model].fields[field])
|
|
72
|
+
*
|
|
73
|
+
* If it's still unclear what this does:
|
|
74
|
+
*
|
|
75
|
+
* 1. User can define a custom fieldName.
|
|
76
|
+
* 2. When using a custom fieldName, doing something like `schema[model].fields[field]` will not work.
|
|
77
|
+
*
|
|
78
|
+
*/
|
|
79
|
+
getDefaultFieldName: ({
|
|
80
|
+
model,
|
|
81
|
+
field,
|
|
82
|
+
}: {
|
|
83
|
+
model: string;
|
|
84
|
+
field: string;
|
|
85
|
+
}) => string;
|
|
86
|
+
/**
|
|
87
|
+
* Get the field attributes for a given model and field.
|
|
88
|
+
*
|
|
89
|
+
* Note: any model name or field name is allowed, whether default to schema or not.
|
|
90
|
+
*/
|
|
91
|
+
getFieldAttributes: ({
|
|
92
|
+
model,
|
|
93
|
+
field,
|
|
94
|
+
}: {
|
|
95
|
+
model: string;
|
|
96
|
+
field: string;
|
|
97
|
+
}) => DBFieldAttribute;
|
|
98
|
+
// The following functions are exposed primarily for the purpose of having wrapper adapters.
|
|
99
|
+
transformInput: (
|
|
100
|
+
data: Record<string, any>,
|
|
101
|
+
defaultModelName: string,
|
|
102
|
+
action: "create" | "update",
|
|
103
|
+
forceAllowId?: boolean | undefined,
|
|
104
|
+
) => Promise<Record<string, any>>;
|
|
105
|
+
transformOutput: (
|
|
106
|
+
data: Record<string, any>,
|
|
107
|
+
defaultModelName: string,
|
|
108
|
+
select?: string[] | undefined,
|
|
109
|
+
joinConfig?: JoinConfig | undefined,
|
|
110
|
+
) => Promise<Record<string, any>>;
|
|
111
|
+
transformWhereClause: <W extends Where[] | undefined>({
|
|
112
|
+
model,
|
|
113
|
+
where,
|
|
114
|
+
}: {
|
|
115
|
+
where: W;
|
|
116
|
+
model: string;
|
|
117
|
+
}) => W extends undefined ? undefined : CleanedWhere[];
|
|
118
|
+
}) => CustomAdapter;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @deprecated Use `CustomAdapter` from `@better-auth/core/db/adapter` instead.
|
|
122
|
+
*/
|
|
123
|
+
export interface CustomAdapter extends Omit<CoreCustomAdapter, "createSchema"> {
|
|
124
|
+
createSchema?:
|
|
125
|
+
| ((props: {
|
|
126
|
+
/**
|
|
127
|
+
* The file the user may have passed in to the `generate` command as the expected schema file output path.
|
|
128
|
+
*/
|
|
129
|
+
file?: string;
|
|
130
|
+
/**
|
|
131
|
+
* The tables from the user's Better-Auth instance schema.
|
|
132
|
+
*/
|
|
133
|
+
tables: BetterAuthDBSchema;
|
|
134
|
+
}) => Promise<AdapterSchemaCreation>)
|
|
135
|
+
| undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* @deprecated Use `CleanedWhere` from `@better-auth/core/db/adapter` instead.
|
|
140
|
+
*/
|
|
141
|
+
export type CleanedWhere = Prettify<Required<Where>>;
|
|
142
|
+
|
|
143
|
+
export type AdapterTestDebugLogs = {
|
|
144
|
+
resetDebugLogs: () => void;
|
|
145
|
+
printDebugLogs: () => void;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* @deprecated Use `AdapterFactoryOptions` instead. This export will be removed in a future version.
|
|
150
|
+
*/
|
|
151
|
+
export type CreateAdapterOptions = AdapterFactoryOptions;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* @deprecated Use `AdapterFactoryConfig` instead. This export will be removed in a future version.
|
|
155
|
+
*/
|
|
156
|
+
export type AdapterConfig = AdapterFactoryConfig;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @deprecated Use `AdapterFactoryCustomizeAdapterCreator` instead. This export will be removed in a future version.
|
|
160
|
+
*/
|
|
161
|
+
export type CreateCustomAdapter = AdapterFactoryCustomizeAdapterCreator;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { DBFieldAttribute } from "../type";
|
|
2
|
+
|
|
3
|
+
export function withApplyDefault(
|
|
4
|
+
value: any,
|
|
5
|
+
field: DBFieldAttribute,
|
|
6
|
+
action: "create" | "update" | "findOne" | "findMany",
|
|
7
|
+
) {
|
|
8
|
+
if (action === "update") {
|
|
9
|
+
// Apply onUpdate if value is undefined
|
|
10
|
+
if (value === undefined && field.onUpdate !== undefined) {
|
|
11
|
+
if (typeof field.onUpdate === "function") {
|
|
12
|
+
return field.onUpdate();
|
|
13
|
+
}
|
|
14
|
+
return field.onUpdate;
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (action === "create") {
|
|
19
|
+
// we do not want to apply default values if the value is null & not required
|
|
20
|
+
if (value === undefined || (field.required === true && value === null)) {
|
|
21
|
+
if (field.defaultValue !== undefined) {
|
|
22
|
+
if (typeof field.defaultValue === "function") {
|
|
23
|
+
return field.defaultValue();
|
|
24
|
+
}
|
|
25
|
+
return field.defaultValue;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isObject(item: unknown): item is Record<string, unknown> {
|
|
33
|
+
return item !== null && typeof item === "object" && !Array.isArray(item);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function deepmerge<T>(target: T, source: Partial<T>): T {
|
|
37
|
+
if (Array.isArray(target) && Array.isArray(source)) {
|
|
38
|
+
// merge arrays by concatenation
|
|
39
|
+
return [...target, ...source] as T;
|
|
40
|
+
} else if (isObject(target) && isObject(source)) {
|
|
41
|
+
const result: Record<string, unknown> = { ...target };
|
|
42
|
+
|
|
43
|
+
for (const [key, value] of Object.entries(source)) {
|
|
44
|
+
if (value === undefined) continue; // skip undefined
|
|
45
|
+
|
|
46
|
+
if (key in target) {
|
|
47
|
+
result[key] = deepmerge(
|
|
48
|
+
(target as Record<string, unknown>)[key],
|
|
49
|
+
value as unknown as Partial<T>,
|
|
50
|
+
);
|
|
51
|
+
} else {
|
|
52
|
+
result[key] = value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result as T;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// primitives and fallback: source overrides target
|
|
60
|
+
return source as T;
|
|
61
|
+
}
|