@docyrus/tanstack-db-generator
Version:
Code generator utilities for TanStack Query / Database integration with Docyrus API
204 lines (201 loc) • 8.24 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateCollections = generateCollections;
const list_parameter_definitions_1 = require("../list-parameter-definitions");
const fs_1 = require("fs");
const path_1 = require("path");
const typegen_1 = require("../utils/typegen");
async function generateCollections(spec, dataSources, outputDir) {
// Generate individual collection files
for (const dataSource of dataSources) {
const collectionContent = generateCollectionFile(spec, dataSource);
const fileName = `${dataSource.appName}-${dataSource.dataSourceName}.collection.ts`;
(0, fs_1.writeFileSync)((0, path_1.join)(outputDir, fileName), collectionContent);
}
// Generate index file
const indexContent = generateCollectionsIndex(dataSources);
(0, fs_1.writeFileSync)((0, path_1.join)(outputDir, 'index.ts'), indexContent);
}
function generateCollectionFile(spec, dataSource) {
const { appName, dataSourceName, entityName, endpoints } = dataSource;
const collectionName = `${appName}${toPascalCase(dataSourceName)}Collection`;
const methods = [];
if (endpoints.list) {
methods.push(generateListMethod(spec, endpoints.list, entityName));
}
if (endpoints.get) {
methods.push(generateGetMethod(spec, endpoints.get, entityName));
}
if (endpoints.create) {
methods.push(generateCreateMethod(spec, endpoints.create, entityName));
}
if (endpoints.update) {
methods.push(generateUpdateMethod(spec, endpoints.update, entityName));
}
if (endpoints.delete) {
methods.push(generateDeleteMethod(spec, endpoints.delete));
}
if (endpoints.deleteMany) {
methods.push(generateDeleteManyMethod(spec, endpoints.deleteMany));
}
const inlineTypes = (0, typegen_1.generateInterfacesForNames)(spec, [entityName]);
return `// Generated collection for ${appName}/${dataSourceName}
import { apiClient } from '../lib/api';
${inlineTypes ? inlineTypes + '\n' : ''}
export const ${collectionName} = {
${methods.join(',\n\n')}
};
`;
}
function generateListMethod(spec, endpoint, entityName) {
const jsDoc = buildDsListJsDoc();
const paramsType = buildListParamsType();
const url = endpoint.path;
return ` ${jsDoc}list: (params?: ${paramsType}): Promise<Array<${entityName}>> => {
const { ['x-connection-id']: xConnectionId, ['x-connection-account-id']: xConnectionAccountId, ...query } = params || {} as any;
return apiClient.get('${url}', query);
}`;
}
function generateGetMethod(spec, endpoint, entityName) {
const jsDoc = buildDsJsDoc(spec, endpoint, entityName);
return ` ${jsDoc}get: (recordId: string, params?: { columns?: Array<string> }): Promise<${entityName}> =>
apiClient.get('${endpoint.path}'.replace('{recordId}', recordId), params)`;
}
function generateCreateMethod(spec, endpoint, entityName) {
const jsDoc = buildDsJsDoc(spec, endpoint, entityName);
return ` ${jsDoc}create: (data: { data: any }): Promise<${entityName}> =>
apiClient.post('${endpoint.path}', data)`;
}
function generateUpdateMethod(spec, endpoint, entityName) {
const jsDoc = buildDsJsDoc(spec, endpoint, entityName);
return ` ${jsDoc}update: (recordId: string, data: { data: any }): Promise<${entityName}> =>
apiClient.patch('${endpoint.path}'.replace('{recordId}', recordId), data)`;
}
function generateDeleteMethod(spec, endpoint) {
const jsDoc = buildDsJsDoc(spec, endpoint, 'void');
return ` ${jsDoc}delete: (recordId: string): Promise<void> =>
apiClient.delete('${endpoint.path}'.replace('{recordId}', recordId))`;
}
function generateDeleteManyMethod(spec, endpoint) {
const jsDoc = buildDsJsDoc(spec, endpoint, 'void');
return ` ${jsDoc}deleteMany: (data: { recordIds: Array<string> }): Promise<void> =>
apiClient.delete('${endpoint.path}', data)`;
}
function generateCollectionsIndex(dataSources) {
const exports = dataSources.map(ds => {
const collectionName = `${ds.appName}${toPascalCase(ds.dataSourceName)}Collection`;
const fileName = `${ds.appName}-${ds.dataSourceName}.collection`;
return `export { ${collectionName} } from './${fileName}';`;
});
return `// Generated collections index
${exports.join('\n')}
`;
}
function toPascalCase(str) {
return str.split(/[_-]/)
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
.join('');
}
function buildDsJsDoc(spec, endpoint, entityName, container) {
const lines = [];
if (endpoint.summary)
lines.push(sanitizeJsDoc(endpoint.summary));
if (endpoint.description && endpoint.description !== endpoint.summary)
lines.push(sanitizeJsDoc(endpoint.description));
// Param docs (only for the subset our signatures accept)
const paramObjs = (endpoint.parameters || []).map((p) => resolveParameter(p, spec)).filter(Boolean);
const byName = new Map(paramObjs.map(p => [p.name, p]));
const docParam = (name, label) => {
const p = byName.get(name);
if (p?.description)
lines.push(`@param ${label || name} - ${sanitizeJsDoc(p.description)}`);
};
// For list
docParam('limit', 'params.limit');
docParam('offset', 'params.offset');
docParam('columns', 'params.columns');
docParam('filters', 'params.filters');
// For record id
docParam('recordId', 'recordId');
// For request body
if (endpoint.requestBody)
lines.push(`@param data - Request body`);
// Return type
if (entityName) {
if (entityName === 'void')
lines.push(`@returns void`);
else if (container === 'Array')
lines.push(`@returns Array<${entityName}>`);
else
lines.push(`@returns ${entityName}`);
}
if (lines.length === 0)
return '';
return `/**\n * ${lines.join('\n * ')}\n */\n `;
}
function resolveParameter(p, spec) {
if (p && p.$ref) {
const ref = p.$ref;
const path = ref.replace('#/', '').split('/');
let current = spec;
for (const seg of path)
current = current?.[seg];
return current || null;
}
return p;
}
function sanitizeJsDoc(text) {
return String(text).replace(/\*\//g, '*\/').trim();
}
function buildListParamsType() {
const p = list_parameter_definitions_1.listParameterDefinitions;
// Build a flat object type for query + header params used in list endpoints.
const fields = [];
const add = (name, type) => fields.push(`${JSON.stringify(name)}?: ${type}`);
// Query parameters
add('columns', 'Array<string>');
add('distinctColumns', 'Array<string>');
add('rows', 'Array<string>');
add('formulas', 'string');
add('calculations', 'Array<any>');
add('filters', 'any');
add('filterKeyword', 'string');
add('orderBy', 'string');
add('limit', 'number');
add('offset', 'number');
add('fullCount', 'boolean');
add('expand', 'string');
// Header parameters: keep original header names
add('x-connection-id', 'string');
add('x-connection-account-id', 'string');
return `{ ${fields.join('; ')} }`;
}
function buildDsListJsDoc() {
const p = list_parameter_definitions_1.listParameterDefinitions;
const lines = [];
lines.push('List data source records based on specified columns, filters, and other configuration options');
const addParam = (key, label) => {
const def = p[key];
if (!def)
return;
const name = label || def.name;
const desc = def.description ? ` - ${sanitizeJsDoc(def.description)}` : '';
lines.push(`@param params.${name}${desc}`.trim());
};
addParam('Columns');
addParam('DistinctColumns');
addParam('Rows');
addParam('Formulas');
addParam('Calculations');
addParam('Filters');
addParam('FilterKeyword');
addParam('OrderBy');
addParam('Limit');
addParam('Offset');
addParam('FullCount');
addParam('Expand');
// Headers
addParam('XConnectionId', 'x-connection-id');
addParam('XConnectionAccountId', 'x-connection-account-id');
return `/**\n * ${lines.join('\n * ')}\n */\n `;
}