Skip to main content
Glama

Convex MCP server

Official
by get-convex
explicit-ids.ts4.05 kB
import { Context, startLogProgress } from "@/context"; import { deleteResolveTypesTempFile, resolveTypes } from "@/util/resolveTypes"; import path from "node:path"; import { CallExpression, Project, SourceFile, SyntaxKind, ts, Type, } from "ts-morph"; export const CONVEX_VERSION_RANGE = ">=1.99.0"; export async function explicitIds( ctx: Context, project: Project, root: string, ) { const progress = startLogProgress( ctx, "[:bar] :percent (:current/:total) Processing :name…", { total: project.getSourceFiles().length, }, ); for (const sourceFile of project.getSourceFiles()) { progress.tick({ width: 30, name: path.relative(root, sourceFile.getFilePath()), }); // Note: we create and delete the temporary file for every file in the // source tree. This is intentional, because it looks like if we only // create it once, the resolved types break when processing the next file // after modifying the first one. const { anyDatabaseReader, anyDatabaseWriter, id } = resolveTypes( project, root, ); const dbCalls = findDbCalls( sourceFile, anyDatabaseReader, anyDatabaseWriter, ); for (const call of dbCalls) { updateDbCall(ctx, call, id); } deleteResolveTypesTempFile(project, root); await allowInterrupt(); } } function findDbCalls( sourceFile: SourceFile, anyDatabaseReader: Type, anyDatabaseWriter: Type, ): CallExpression<ts.CallExpression>[] { return sourceFile .getDescendantsOfKind(SyntaxKind.CallExpression) .filter((node) => { const callee = node.getExpression(); // Check if the callee is a property access expression (XXX.get) const propertyAccess = callee.asKind(SyntaxKind.PropertyAccessExpression); if (!propertyAccess) { return false; } const methodName = propertyAccess.getNameNode().getText(); if (!["get", "replace", "patch", "delete"].includes(methodName)) { return false; } const dbType = propertyAccess.getExpression().getType(); const isDbCall = (dbType.isAssignableTo(anyDatabaseReader) && methodName === "get") || dbType.isAssignableTo(anyDatabaseWriter); if (!isDbCall) { return false; } // Is it an unmigrated call? const args = node.getArguments(); return ( (methodName === "get" && args.length === 1) || (methodName === "replace" && args.length === 2) || (methodName === "patch" && args.length === 2) || (methodName === "delete" && args.length === 1) ); }); } function updateDbCall( ctx: Context, call: CallExpression<ts.CallExpression>, id: Type, ) { const idArg = call.getArguments()[0]!; const idType = idArg.getType(); if (!idType.isAssignableTo(id)) { ctx.addWarning({ title: "Can’t update call site", message: `Expected \`${idArg.getText()}\` to be an \`Id<T>\`, but it is an \`${idType.getText()}\` instead.`, node: idArg, }); return; } const typeArguments = idType.getAliasTypeArguments(); if (typeArguments.length !== 1) { ctx.addWarning({ title: "Can’t update call site", message: `Sorry, we can’t infer the table type of \`${idArg.getText()}\` (which is a \`${idType.getText()}\`).`, node: idArg, }); return; } const tableName = typeArguments[0]; if (!tableName.isStringLiteral()) { ctx.addWarning({ title: "Can’t update call site", message: `Expected \`${idArg.getText()}\` to be an \`Id<T>\`, where \`T\` is a string literal, but got \`T = ${tableName.getText()}\` instead.`, node: idArg, }); return; } call.insertArgument(0, tableName.getText()); ctx.incrementChanges(call.getSourceFile().getFilePath()); } /** * Awaiting this function in a loop allows us to make sure the user * can interrupt the codemod with CTRL+C */ async function allowInterrupt() { await new Promise((resolve) => setTimeout(resolve, 0)); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/get-convex/convex-backend'

If you have feedback or need assistance with the MCP directory API, please join our Discord server