better-auth
Version:
The most comprehensive authentication library for TypeScript.
260 lines (257 loc) • 8.4 kB
JavaScript
import { ObjectId } from 'mongodb';
import { c as createAdapter } from './better-auth.DOXaXTC6.mjs';
const mongodbAdapter = (db, config) => {
const getCustomIdGenerator = (options) => {
const generator = options.advanced?.database?.generateId || options.advanced?.generateId;
if (typeof generator === "function") {
return generator;
}
return void 0;
};
return createAdapter({
config: {
adapterId: "mongodb-adapter",
adapterName: "MongoDB Adapter",
usePlural: config?.usePlural ?? false,
debugLogs: config?.debugLogs ?? false,
mapKeysTransformInput: {
id: "_id"
},
mapKeysTransformOutput: {
_id: "id"
},
supportsNumericIds: false,
customTransformInput({
action,
data,
field,
fieldAttributes,
schema,
model,
options
}) {
const customIdGen = getCustomIdGenerator(options);
if (field === "_id" || fieldAttributes.references?.field === "id") {
if (customIdGen) {
return data;
}
if (action === "update") {
return data;
}
if (Array.isArray(data)) {
return data.map((v) => new ObjectId());
}
if (typeof data === "string") {
try {
return new ObjectId(data);
} catch (error) {
return new ObjectId();
}
}
return new ObjectId();
}
return data;
},
customTransformOutput({ data, field, fieldAttributes }) {
if (field === "id" || fieldAttributes.references?.field === "id") {
if (data instanceof ObjectId) {
return data.toHexString();
}
if (Array.isArray(data)) {
return data.map((v) => {
if (v instanceof ObjectId) {
return v.toHexString();
}
return v;
});
}
return data;
}
return data;
}
},
adapter: ({ options, getFieldName, schema, getDefaultModelName }) => {
const customIdGen = getCustomIdGenerator(options);
function serializeID({
field,
value,
model
}) {
if (customIdGen) {
return value;
}
model = getDefaultModelName(model);
if (field === "id" || field === "_id" || schema[model].fields[field]?.references?.field === "id") {
if (typeof value !== "string") {
if (value instanceof ObjectId) {
return value;
}
if (Array.isArray(value)) {
return value.map((v) => {
if (typeof v === "string") {
try {
return new ObjectId(v);
} catch (e) {
return v;
}
}
if (v instanceof ObjectId) {
return v;
}
throw new Error("Invalid id value");
});
}
throw new Error("Invalid id value");
}
try {
return new ObjectId(value);
} catch (e) {
return value;
}
}
return value;
}
function convertWhereClause({
where,
model
}) {
if (!where.length) return {};
const conditions = where.map((w) => {
const {
field: field_,
value,
operator = "eq",
connector = "AND"
} = w;
let condition;
let field = getFieldName({ model, field: field_ });
if (field === "id") field = "_id";
switch (operator.toLowerCase()) {
case "eq":
condition = {
[field]: serializeID({
field,
value,
model
})
};
break;
case "in":
condition = {
[field]: {
$in: Array.isArray(value) ? value.map((v) => serializeID({ field, value: v, model })) : [serializeID({ field, value, model })]
}
};
break;
case "not_in":
condition = {
[field]: {
$nin: Array.isArray(value) ? value.map((v) => serializeID({ field, value: v, model })) : [serializeID({ field, value, model })]
}
};
break;
case "gt":
condition = { [field]: { $gt: value } };
break;
case "gte":
condition = { [field]: { $gte: value } };
break;
case "lt":
condition = { [field]: { $lt: value } };
break;
case "lte":
condition = { [field]: { $lte: value } };
break;
case "ne":
condition = { [field]: { $ne: value } };
break;
case "contains":
condition = { [field]: { $regex: `.*${value}.*` } };
break;
case "starts_with":
condition = { [field]: { $regex: `${value}.*` } };
break;
case "ends_with":
condition = { [field]: { $regex: `.*${value}` } };
break;
default:
throw new Error(`Unsupported operator: ${operator}`);
}
return { condition, connector };
});
if (conditions.length === 1) {
return conditions[0].condition;
}
const andConditions = conditions.filter((c) => c.connector === "AND").map((c) => c.condition);
const orConditions = conditions.filter((c) => c.connector === "OR").map((c) => c.condition);
let clause = {};
if (andConditions.length) {
clause = { ...clause, $and: andConditions };
}
if (orConditions.length) {
clause = { ...clause, $or: orConditions };
}
return clause;
}
return {
async create({ model, data: values }) {
const res = await db.collection(model).insertOne(values);
const insertedData = { _id: res.insertedId.toString(), ...values };
return insertedData;
},
async findOne({ model, where, select }) {
const clause = convertWhereClause({ where, model });
const res = await db.collection(model).findOne(clause);
if (!res) return null;
return res;
},
async findMany({ model, where, limit, offset, sortBy }) {
const clause = where ? convertWhereClause({ where, model }) : {};
const cursor = db.collection(model).find(clause);
if (limit) cursor.limit(limit);
if (offset) cursor.skip(offset);
if (sortBy)
cursor.sort(
getFieldName({ field: sortBy.field, model }),
sortBy.direction === "desc" ? -1 : 1
);
const res = await cursor.toArray();
return res;
},
async count({ model }) {
const res = await db.collection(model).countDocuments();
return res;
},
async update({ model, where, update: values }) {
const clause = convertWhereClause({ where, model });
const res = await db.collection(model).findOneAndUpdate(
clause,
{ $set: values },
{
returnDocument: "after"
}
);
if (!res) return null;
return res;
},
async updateMany({ model, where, update: values }) {
const clause = convertWhereClause({ where, model });
const res = await db.collection(model).updateMany(clause, {
$set: values
});
return res.modifiedCount;
},
async delete({ model, where }) {
const clause = convertWhereClause({ where, model });
await db.collection(model).deleteOne(clause);
},
async deleteMany({ model, where }) {
const clause = convertWhereClause({ where, model });
const res = await db.collection(model).deleteMany(clause);
return res.deletedCount;
}
};
}
});
};
export { mongodbAdapter as m };