@convex-dev/better-auth
Version:
A Better Auth component for Convex.
114 lines (110 loc) • 4.78 kB
JavaScript
// Manually add fields to index on for schema generation,
// all fields in the schema specialFields are automatically indexed
export const indexFields = {
account: ["accountId", ["accountId", "providerId"], ["providerId", "userId"]],
rateLimit: ["key"],
session: ["expiresAt", ["expiresAt", "userId"]],
verification: ["expiresAt", "identifier"],
user: [["email", "name"], "name", "userId"],
passkey: ["credentialID"],
oauthConsent: [["clientId", "userId"]],
};
// Return map of unique, sortable, and reference fields
const specialFields = (tables) => Object.fromEntries(Object.entries(tables)
.map(([key, table]) => {
const fields = Object.fromEntries(Object.entries(table.fields)
.map(([fieldKey, field]) => [
field.fieldName ?? fieldKey,
{
...(field.sortable ? { sortable: true } : {}),
...(field.unique ? { unique: true } : {}),
...(field.references ? { references: field.references } : {}),
},
])
.filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true));
return [key, fields];
})
.filter(([_key, value]) => typeof value === "object" ? Object.keys(value).length > 0 : true));
const mergedIndexFields = (tables) => Object.fromEntries(Object.entries(tables).map(([key, table]) => {
const manualIndexes = indexFields[key]?.map((index) => {
return typeof index === "string"
? (table.fields[index]?.fieldName ?? index)
: index.map((i) => table.fields[i]?.fieldName ?? i);
}) || [];
const specialFieldIndexes = Object.keys(specialFields(tables)[key] ||
{}).filter((index) => !manualIndexes.some((m) => Array.isArray(m) ? m[0] === index : m === index));
return [key, manualIndexes.concat(specialFieldIndexes)];
}));
export const createSchema = async ({ file, tables, }) => {
// stop convex esbuild from throwing over this import, only runs
// in the better auth cli
const pathImport = "path";
const path = await import(pathImport);
const baseName = path.basename(path.resolve(process.cwd(), file ?? ""));
// if the target directory is named "convex", they're almost definitely
// generating the schema in the wrong directory, likely would replace the
// app schema
if (baseName === "convex") {
throw new Error("Better Auth schema must be generated in the Better Auth component directory.");
}
let code = `/**
* This file is auto-generated. Do not edit this file manually.
* To regenerate the schema, run:
* \`npx @better-auth/cli generate --output ${file} -y\`
*
* To customize the schema, generate to an alternate file and import
* the table definitions to your schema file. See
* https://labs.convex.dev/better-auth/features/local-install#adding-custom-indexes.
*/
import { defineSchema, defineTable } from "convex/server";
import { v } from "convex/values";
export const tables = {
`;
for (const tableKey in tables) {
const table = tables[tableKey];
const modelName = table.modelName;
// No id fields in Convex schema
const fields = Object.fromEntries(Object.entries(table.fields).filter(([key]) => key !== "id"));
function getType(name, field) {
const type = field.type;
const typeMap = {
string: `v.string()`,
boolean: `v.boolean()`,
number: `v.number()`,
date: `v.number()`,
json: `v.string()`,
"number[]": `v.array(v.number())`,
"string[]": `v.array(v.string())`,
};
return typeMap[type];
}
const indexes = mergedIndexFields(tables)[tableKey]?.map((index) => {
const indexArray = Array.isArray(index) ? index.sort() : [index];
const indexName = indexArray.join("_");
return `.index("${indexName}", ${JSON.stringify(indexArray)})`;
}) || [];
const schema = `${modelName}: defineTable({
${Object.keys(fields)
.map((field) => {
const attr = fields[field];
const type = getType(field, attr);
const optional = (fieldSchema) => attr.required
? fieldSchema
: `v.optional(v.union(v.null(), ${fieldSchema}))`;
return ` ${attr.fieldName ?? field}: ${optional(type)},`;
})
.join("\n")}
})${indexes.length > 0 ? `\n ${indexes.join("\n ")}` : ""},\n`;
code += ` ${schema}`;
}
code += `};
const schema = defineSchema(tables);
export default schema;
`;
return {
code,
path: file ?? "./schema.ts",
overwrite: true,
};
};
//# sourceMappingURL=create-schema.js.map