@patchworkdev/pdk
Version:
Patchwork Development Kit
151 lines (148 loc) • 5.61 kB
JavaScript
;
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;
}