UNPKG

@patchworkdev/pdk

Version:

Patchwork Development Kit

151 lines (148 loc) 5.62 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateAPI = generateAPI; const promises_1 = __importDefault(require("fs/promises")); const path_1 = __importDefault(require("path")); const config_1 = require("../../common/helpers/config"); // import { FieldDefinition, Schema } from "./ponderMocks"; const file_1 = require("../../common/helpers/file"); const logger_1 = require("../../common/helpers/logger"); async function generateAPI(ponderSchema, apiOutputDir) { const schema = await (0, config_1.loadPonderSchema)(ponderSchema); // Check if the API output directory exists, create it if it doesn't try { await promises_1.default.access(apiOutputDir); } catch (error) { logger_1.logger.info(`API output directory does not exist. Creating ${apiOutputDir}`); await promises_1.default.mkdir(apiOutputDir, { recursive: true }); } // Generate the tRPC API content const output = await generateTrpcApi(schema); // Write the formatted API content to file const outputPath = path_1.default.join(apiOutputDir, 'api.ts'); // await fs.writeFile(outputPath, apiContent, 'utf8'); await (0, file_1.formatAndSaveFile)(outputPath, output.join('\n')); logger_1.logger.info(`tRPC API generation completed. Output written to ${outputPath}`); } function getZodType(fieldDef) { switch (fieldDef.type) { case 'bigint': return 'z.bigint()'; case 'int': return 'z.number().int()'; case 'hex': return 'z.string().regex(/^0x[a-fA-F0-9]+$/)'; case 'boolean': return 'z.boolean()'; case 'string': return 'z.string()'; default: return 'z.unknown()'; } } function generateFilterInput(tableName, tableDefinition) { const filterFields = Object.entries(tableDefinition) .filter(([_, fieldDef]) => fieldDef.type !== 'many' && fieldDef.type !== 'one') .map(([fieldName, fieldDef]) => { const zodType = getZodType(fieldDef); return `${fieldName}: ${zodType}.optional(),`; }) .join('\n '); return `z.object({ limit: z.number().min(1).max(100).default(10), lastTimestamp: z.number().optional(), ${filterFields} })`; } function generateWhereClause(tableName, tableDefinition) { const filterConditions = Object.entries(tableDefinition) .filter(([_, fieldDef]) => fieldDef.type !== 'many' && fieldDef.type !== 'one') .map(([fieldName, fieldDef]) => { if (fieldDef.type === 'hex') { return `input.${fieldName} !== undefined ? eq(${tableName.toLowerCase()}.${fieldName}, input.${fieldName} as \`0x\${string}\`) : undefined`; } return `input.${fieldName} !== undefined ? eq(${tableName.toLowerCase()}.${fieldName}, input.${fieldName}) : undefined`; }) .join(',\n '); return ` and( lastTimestamp ? gt(${tableName.toLowerCase()}.timestamp, BigInt(lastTimestamp)) : undefined, ${filterConditions} ) `; } async function generateTrpcApi(schema) { let apiContent = [ ` import { eq, gt, and } from "@ponder/core"; import { publicProcedure, router } from "./trpc"; import { z } from "zod";`, ]; const imports = new Set(); const apiObject = ` export const api = { ${Object.entries(schema) .filter((entry) => entry[1].type === 'table') .map(([tableName, entity]) => { // if (entity.type === "enum") { // return ""; // } const tableDefinition = entity._schema; const filterInput = generateFilterInput(tableName, tableDefinition); const whereClause = generateWhereClause(tableName, tableDefinition); imports.add(tableName); // const typeName = pascalCase(tableName); return ` ${tableName}: router({ getById: publicProcedure .input(z.string()) .query(async ({ input, ctx }) => { const result = await ctx.db .select() .from(${tableName}) .where((${tableName.toLowerCase()}) => eq(${tableName.toLowerCase()}.id, input)) .limit(1); return result[0] || null; }), getPaginated: publicProcedure .input(${filterInput}) .query(async ({ input, ctx }) => { const { limit, lastTimestamp, ...filters } = input; const query = ctx.db .select() .from(${tableName}) .where((${tableName.toLowerCase()}) => ${whereClause} ) .orderBy(${tableName}.timestamp) .limit(limit + 1); const items = await query; let nextTimestamp: number | undefined = undefined; if (items.length > limit) { const nextItem = items.pop(); nextTimestamp = nextItem?.timestamp ? Number(nextItem.timestamp) : undefined; } return { items, nextTimestamp, }; }), }), `; }) .join('')} }; `; apiContent.push(`import {${[...imports].sort().join(',')}} from "../../ponder.schema"`); // apiContent.push(`type GetPaginatedOutput<T> = { items: T[], nextTimestamp: number | undefined }`); // [...imports].forEach((i) => { // apiContent.push(`type ${_.upperFirst(_.camelCase(i))} = typeof ${i}.$inferSelect;`); // }); apiContent.push(); apiContent.push(apiObject); return apiContent; }