UNPKG

@martinmilo/verve

Version:

TypeScript domain modeling library with field-level authorization, business rule validation, and context-aware access control

769 lines 35.5 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ModelTypeGenerator = void 0; exports.generateTypes = generateTypes; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const url_1 = require("url"); class ModelTypeGenerator { constructor(rootPath) { this.rootPath = rootPath; this.models = []; this.processedFiles = new Set(); this.orphanedImports = []; // Imports from files where we couldn't extract models // Register ts-node for TypeScript compilation try { require('ts-node').register({ transpileOnly: true, compilerOptions: { module: 'commonjs', target: 'es2017', esModuleInterop: true, allowSyntheticDefaultImports: true } }); } catch (error) { console.warn('Warning: Could not register ts-node. TypeScript files may not be processed.'); } } async generateTypes() { await this.scanDirectory(this.rootPath); const content = await this.writeTypesFile(); await this.updateModelClasses(); return content; } async scanDirectory(dirPath) { const entries = await fs.promises.readdir(dirPath, { withFileTypes: true }); for (const entry of entries) { const fullPath = path.join(dirPath, entry.name); if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') { await this.scanDirectory(fullPath); } else if (entry.isFile() && (entry.name.endsWith('.ts') || entry.name.endsWith('.js'))) { await this.parseFile(fullPath); } } } async parseFile(filePath) { if (this.processedFiles.has(filePath)) return; this.processedFiles.add(filePath); try { const content = await fs.promises.readFile(filePath, 'utf-8'); // Quick check if file contains @model decorators if (!content.includes('@model')) { return; } await this.extractModelsFromFile(filePath); } catch (error) { console.warn(`Warning: Could not process file ${filePath}:`, error); } } async extractModelsFromFile(filePath) { try { const sourceCode = await fs.promises.readFile(filePath, 'utf-8'); if (filePath.endsWith('.ts')) { // Use direct require with ts-node for TypeScript files try { // Check if file has problematic absolute imports if (this.hasAbsoluteImports(sourceCode)) { // Create a temporary file with absolute imports commented out const tempSourceCode = this.commentOutAbsoluteImports(sourceCode); const tempFilePath = filePath.replace(/\.ts$/, '.temp.ts'); // Keep .ts extension for ts-node try { await fs.promises.writeFile(tempFilePath, tempSourceCode, 'utf-8'); // Clear the require cache delete require.cache[require.resolve(path.resolve(tempFilePath))]; const module = require(path.resolve(tempFilePath)); await this.extractModelsFromModule(module, path.basename(filePath), sourceCode, filePath); // Clean up temp file await fs.promises.unlink(tempFilePath); } catch (tempError) { // Clean up temp file if it exists try { await fs.promises.unlink(tempFilePath); } catch (_a) { } console.warn(`Warning: Could not require TypeScript file ${filePath} (even with temp file):`, tempError); this.extractImportsOnly(sourceCode, filePath); } } else { // No problematic imports, proceed normally delete require.cache[require.resolve(path.resolve(filePath))]; const module = require(path.resolve(filePath)); await this.extractModelsFromModule(module, path.basename(filePath), sourceCode, filePath); } } catch (requireError) { console.warn(`Warning: Could not require TypeScript file ${filePath}:`, requireError); // Even if we can't require the module, we can still extract imports from source code // This is useful when models have unresolvable imports but we still want to copy the type imports this.extractImportsOnly(sourceCode, filePath); } } else { // Handle JavaScript files with dynamic import const absolutePath = path.resolve(filePath); const fileUrl = (0, url_1.pathToFileURL)(absolutePath).href; await this.importAndExtractModels(fileUrl, path.basename(filePath), sourceCode, filePath); } } catch (error) { console.warn(`Warning: Could not process file ${filePath}:`, error); } } async extractModelsFromModule(module, fileName, sourceCode, filePath) { try { // Look for exported classes that have a schema property (indicating they're models) for (const [exportName, exportValue] of Object.entries(module)) { if (this.isModelClass(exportValue)) { const modelInfo = this.extractModelInfo(exportValue, exportName, sourceCode, filePath); if (modelInfo) { this.models.push(modelInfo); console.log(`Found model: ${exportName} in ${fileName}`); } } } } catch (error) { console.warn(`Failed to process module from ${fileName}:`, error); } } async importAndExtractModels(fileUrl, fileName, sourceCode, filePath) { try { const module = await Promise.resolve(`${fileUrl}`).then(s => __importStar(require(s))); await this.extractModelsFromModule(module, fileName, sourceCode, filePath); } catch (error) { // Silently ignore import errors as not all files may be importable } } hasAbsoluteImports(sourceCode) { // Check for imports that are not relative (don't start with . or /) // but exclude 'verve' imports which should work const importPattern = /import\s+[^;]+\s+from\s*['"]([^'"]+)['"]/g; let match; while ((match = importPattern.exec(sourceCode)) !== null) { const importPath = match[1]; // Skip verve imports and relative/absolute file paths if (importPath === 'verve' || importPath.startsWith('.') || importPath.startsWith('/') || importPath.startsWith('node_modules')) { continue; } // This is a problematic absolute import if (!importPath.startsWith('.') && !importPath.startsWith('/')) { return true; } } return false; } commentOutAbsoluteImports(sourceCode) { // First, extract what types are being imported from absolute imports const importedTypes = new Map(); const absoluteImportPattern = /^(\s*import\s+(?:\{([^}]+)\}|\*\s+as\s+(\w+)|(\w+))\s+from\s*['"](?![\.\/])[^'"]+['"]\s*;?\s*)$/gm; let match; while ((match = absoluteImportPattern.exec(sourceCode)) !== null) { if (match[2]) { // Named imports: { Type1, Type2 } const namedImports = match[2].split(',').map(s => s.trim()); namedImports.forEach(imp => { const cleanName = imp.split(' as ')[0].trim(); importedTypes.set(cleanName, 'string'); // Default placeholder type }); } else if (match[3]) { // Namespace import: * as Namespace importedTypes.set(match[3], '{}'); } else if (match[4]) { // Default import importedTypes.set(match[4], 'string'); } } // Comment out absolute imports and replace with placeholders, but keep 'verve' imports let result = sourceCode.replace(/^(\s*import\s+[^;]+\s+from\s*['"]([^'"]+)['"]\s*;?\s*)$/gm, (match, fullMatch, importPath) => { // Don't comment out verve imports or relative imports if (importPath === 'verve' || importPath.startsWith('.') || importPath.startsWith('/') || importPath.startsWith('node_modules')) { return match; // Keep as-is } // Comment out the problematic absolute import and add placeholder declarations right after const commentedImport = `// ${match.trim()}`; // Extract types from this specific import to create placeholders const specificImportMatch = match.match(/import\s+(?:\{([^}]+)\}|\*\s+as\s+(\w+)|(\w+))\s+from/); if (specificImportMatch) { const placeholders = []; if (specificImportMatch[1]) { // Named imports const namedImports = specificImportMatch[1].split(',').map(s => s.trim()); namedImports.forEach(imp => { const cleanName = imp.split(' as ')[0].trim(); placeholders.push(`const ${cleanName} = "placeholder" as any;`); }); } else if (specificImportMatch[2]) { // Namespace import placeholders.push(`const ${specificImportMatch[2]} = {} as any;`); } else if (specificImportMatch[3]) { // Default import placeholders.push(`const ${specificImportMatch[3]} = "placeholder" as any;`); } return commentedImport + '\n' + placeholders.join('\n'); } return commentedImport; }); return result; } extractImportsOnly(sourceCode, filePath) { // Extract imports from files where we couldn't load the module // but still want to capture external type imports for the generated file if (sourceCode.includes('@model')) { const imports = this.extractImports(sourceCode, filePath); if (imports.length > 0) { // Mark these imports with their origin file path for proper resolution const importsWithOrigin = imports.map(imp => ({ ...imp, originFilePath: filePath })); this.orphanedImports.push(...importsWithOrigin); console.log(`Extracted ${imports.length} import(s) from ${path.basename(filePath)} (module load failed)`); } } } isModelClass(value) { return (typeof value === 'function' && value.prototype && value.schema && typeof value.schema === 'object'); } extractModelInfo(ModelClass, className, sourceCode, filePath) { try { const schema = ModelClass.schema; const fields = []; for (const [fieldName, fieldBuilder] of Object.entries(schema)) { if (fieldBuilder && typeof fieldBuilder === 'object' && 'Field' in fieldBuilder) { const field = this.extractFieldInfo(fieldName, fieldBuilder, sourceCode, className); if (field) { fields.push(field); } } } // Extract imports from source code const imports = sourceCode ? this.extractImports(sourceCode, filePath) : []; return { name: className, fields, sourceCode, filePath, imports }; } catch (error) { console.warn(`Warning: Could not extract model info for ${className}:`, error); return null; } } extractImports(sourceCode, filePath) { const imports = []; // Extract all import statements as raw strings, preserving original syntax const importPattern = /^(\s*import\s+[^;]+\s+from\s*['"][^'"]+['"]\s*;?\s*)$/gm; let match; while ((match = importPattern.exec(sourceCode)) !== null) { const fullImportStatement = match[1].trim(); // Parse the import to extract module path and imported types const moduleMatch = fullImportStatement.match(/from\s*['"]([^'"]+)['"]/); if (!moduleMatch) continue; const modulePath = moduleMatch[1]; // Extract imported types for filtering const importedTypes = []; // Named imports: { Type1, Type2 } const namedMatch = fullImportStatement.match(/import\s*\{\s*([^}]+)\s*\}/); if (namedMatch) { const namedImports = namedMatch[1].split(',').map(item => { const trimmed = item.trim(); // Handle alias: 'Type as Alias' return trimmed.includes(' as ') ? trimmed.split(' as ')[1].trim() : trimmed; }); importedTypes.push(...namedImports); } // Default import: import Type from const defaultMatch = fullImportStatement.match(/import\s+(\w+)\s+from/); if (defaultMatch && !namedMatch && !fullImportStatement.includes('* as')) { importedTypes.push(defaultMatch[1]); } // Namespace import: import * as Namespace const namespaceMatch = fullImportStatement.match(/import\s*\*\s*as\s+(\w+)/); if (namespaceMatch) { importedTypes.push(namespaceMatch[1]); } imports.push({ modulePath, importedTypes, isDefault: !!defaultMatch && !namedMatch && !namespaceMatch, alias: namespaceMatch ? namespaceMatch[1] : undefined, originalStatement: fullImportStatement // Store the original import statement }); } return this.filterRelevantImports(imports, filePath); } filterRelevantImports(imports, filePath) { return imports.filter(importInfo => { // Skip imports from the verve library itself if (importInfo.modulePath === 'verve' || importInfo.modulePath.includes('../../../src') || importInfo.modulePath.startsWith('../src')) { return false; } // Skip utility functions and non-type imports const filteredTypes = importInfo.importedTypes.filter(typeName => { const lowerName = typeName.toLowerCase(); if (lowerName.includes('date') && !typeName.match(/^[A-Z]/)) { return false; // Skip utility functions like nowDate } // Include types that start with uppercase (likely types/enums/interfaces) return /^[A-Z]/.test(typeName); }); // Only include this import if it has remaining types after filtering if (filteredTypes.length === 0) { return false; } // Update the import with filtered types, but preserve original statement importInfo.importedTypes = filteredTypes; // Include if it's from type-related paths or has uppercase types (enums, interfaces, etc.) const isTypeRelatedPath = importInfo.modulePath.includes('/enums/') || importInfo.modulePath.includes('/types/') || importInfo.modulePath.includes('/interfaces/') || path.basename(importInfo.modulePath, path.extname(importInfo.modulePath)) === 'types'; return isTypeRelatedPath || filteredTypes.length > 0; }); } resolveImportPath(importPath, fromFile, toFile) { // If it's already a relative path, resolve it relative to fromFile and then make it relative to toFile if (importPath.startsWith('.')) { const absoluteImportPath = path.resolve(path.dirname(fromFile), importPath); const relativeToTarget = path.relative(path.dirname(toFile), absoluteImportPath); return relativeToTarget.startsWith('.') ? relativeToTarget : `./${relativeToTarget}`; } // For absolute module paths (like 'lodash'), return as-is return importPath; } getTargetTypeFromBuilder(fieldBuilder) { try { // Look for the target type stored with the BUILDER_TARGET_TYPE symbol // Since symbols are not enumerable, we need to check all symbol properties const symbols = Object.getOwnPropertySymbols(fieldBuilder); for (const symbol of symbols) { // Check if this symbol looks like our BUILDER_TARGET_TYPE const symbolDescription = symbol.toString(); if (symbolDescription.includes('BuilderTargetType')) { return fieldBuilder[symbol]; } } return null; } catch (error) { return null; } } extractFieldInfo(fieldName, fieldBuilder, sourceCode, className) { try { const Field = fieldBuilder.Field; const options = fieldBuilder.options || {}; // Get the target type from the field builder // The symbol is stored as a property on the fieldBuilder object const targetType = this.getTargetTypeFromBuilder(fieldBuilder); const field = { name: fieldName, type: '', fieldType: Field.name, nullable: options.nullable || false, isArray: false, isRecord: false, defaultValue: options.default }; // Determine TypeScript type based on field class switch (Field.name) { case 'IdField': field.type = 'string'; break; case 'TextField': field.type = 'string'; break; case 'NumberField': field.type = 'number'; break; case 'BoolField': field.type = 'boolean'; break; case 'DateField': field.type = 'Date'; break; case 'ListField': field.isArray = true; // Use target type if available if (targetType) { field.type = `${targetType}[]`; } else { // Extract generic type from source code if available const genericType = this.extractListGenericFromSource(fieldName, sourceCode, className); if (genericType) { field.type = `${genericType}[]`; } else if (options.default && Array.isArray(options.default)) { // Infer from default value const elementType = this.inferTypeFromValue(options.default[0]); field.type = `${elementType}[]`; } else { field.type = 'unknown[]'; } } break; case 'RecordField': field.isRecord = true; // Use target type if available if (targetType) { field.type = targetType; field.recordType = targetType; } else { // Extract generic type from source code if available const genericType = this.extractRecordGenericFromSource(fieldName, sourceCode, className); field.type = genericType || 'Record<string, any>'; field.recordType = genericType || 'Record<string, any>'; } break; case 'OptionField': // Extract enum type from source code if available const enumType = this.extractEnumTypeFromSource(fieldName, sourceCode, className); field.type = enumType || 'string'; field.enumType = enumType || undefined; break; default: field.type = 'unknown'; } // Add null to type if nullable if (field.nullable && !field.isArray) { field.type += ' | null'; } return field; } catch (error) { console.warn(`Warning: Could not extract field info for ${fieldName}:`, error); return null; } } inferTypeFromValue(value) { if (value === null) return 'null'; if (value === undefined) return 'undefined'; const type = typeof value; if (type === 'object') { if (Array.isArray(value)) return 'unknown[]'; if (value instanceof Date) return 'Date'; return 'object'; } return type; } extractEnumTypeFromSource(fieldName, sourceCode, className) { if (!sourceCode || !className) return null; try { // Look for pattern like: fieldName: option(EnumName) const pattern = new RegExp(`${fieldName}:\\s*option\\(\\s*(\\w+)\\s*\\)`); const match = sourceCode.match(pattern); if (match) { const enumName = match[1]; // Check if the enum is defined in the same file if (sourceCode.includes(`enum ${enumName}`)) { return enumName; } // Check for various import patterns and return the enum name if found if (this.isEnumImported(enumName, sourceCode)) { return enumName; } } return null; } catch (error) { return null; } } isEnumImported(enumName, sourceCode) { // Check for various import patterns: // 1. Named import: import { EnumName } from '...' // 2. Named import with alias: import { SomeEnum as EnumName } from '...' // 3. Default import: import EnumName from '...' // 4. Namespace import: import * as Namespace from '...' (then used as Namespace.EnumName) const importPatterns = [ // Named import: import { EnumName } from '...' new RegExp(`import\\s*\\{[^}]*\\b${enumName}\\b[^}]*\\}\\s*from\\s*['"][^'"]+['"]`, 'i'), // Named import with alias: import { SomeEnum as EnumName } from '...' new RegExp(`import\\s*\\{[^}]*\\w+\\s+as\\s+${enumName}\\b[^}]*\\}\\s*from\\s*['"][^'"]+['"]`, 'i'), // Default import: import EnumName from '...' new RegExp(`import\\s+${enumName}\\s+from\\s*['"][^'"]+['"]`, 'i'), // Check if it's part of a namespace import (less common but possible) new RegExp(`import\\s*\\*\\s*as\\s+\\w+\\s+from\\s*['"][^'"]+['"].*${enumName}`, 'i') ]; for (const importPattern of importPatterns) { if (importPattern.test(sourceCode)) { return true; } } // Enhanced fallback: check for any import that includes the enum name // This handles cases where the import might be formatted differently or spread across multiple lines const generalImportPattern = new RegExp(`import[^;]*${enumName}[^;]*from\\s*['"][^'"]*['"]`, 'i'); if (generalImportPattern.test(sourceCode)) { return true; } // Check for multiline imports (common in prettier-formatted code) const multilineImportPattern = new RegExp(`import\\s*\\{[^}]*${enumName}[^}]*\\}\\s*from[^;]*;`, 'gs' // global and dotall flags to match across lines ); if (multilineImportPattern.test(sourceCode)) { return true; } return false; } extractRecordGenericFromSource(fieldName, sourceCode, className) { if (!sourceCode || !className) return null; try { // Look for pattern like: fieldName: record<{ type: definition }>() const pattern = new RegExp(`${fieldName}:\\s*record\\s*<\\s*([^>]+)\\s*>\\s*\\(`); const match = sourceCode.match(pattern); if (match) { return match[1].trim(); } return null; } catch (error) { return null; } } extractListGenericFromSource(fieldName, sourceCode, className) { if (!sourceCode || !className) return null; try { // Look for pattern like: fieldName: list<ElementType>() const pattern = new RegExp(`${fieldName}:\\s*list\\s*<\\s*([^>]+)\\s*>\\s*\\(`); const match = sourceCode.match(pattern); if (match) { return match[1].trim(); } return null; } catch (error) { return null; } } extractEnumDefinitions() { const enumDefinitions = []; const foundEnums = new Set(); for (const model of this.models) { if (!model.sourceCode) continue; // Find enum definitions in the source code const enumRegex = /export\s+enum\s+(\w+)\s*\{([^}]+)\}/g; let match; while ((match = enumRegex.exec(model.sourceCode)) !== null) { const enumName = match[1]; if (!foundEnums.has(enumName)) { foundEnums.add(enumName); const enumContent = match[2]; enumDefinitions.push(`export enum ${enumName} {${enumContent}}`); } } } return enumDefinitions; } collectImports(typesFile) { const importMap = new Map(); // Get all model names to exclude them from imports const modelNames = new Set(this.models.map(model => model.name)); // Process imports from successfully loaded models for (const model of this.models) { if (model.imports && model.filePath) { for (const importInfo of model.imports) { this.addImportToMap(importInfo, model.filePath, typesFile, modelNames, importMap); } } } // Process orphaned imports from failed model loads for (const importInfo of this.orphanedImports) { // Use the stored origin file path for proper import resolution const filePath = importInfo.originFilePath || this.rootPath; this.addImportToMap(importInfo, filePath, typesFile, modelNames, importMap); } return Array.from(importMap.values()); } addImportToMap(importInfo, fromFilePath, typesFile, modelNames, importMap) { // Filter out model names from imported types const filteredTypes = importInfo.importedTypes.filter(typeName => !modelNames.has(typeName)); // Skip this import if no types remain after filtering if (filteredTypes.length === 0) { return; } // Resolve the import path relative to the generated types file const resolvedPath = this.resolveImportPath(importInfo.modulePath, fromFilePath, typesFile); // Create a unique key for deduplication const key = `${resolvedPath}:${importInfo.isDefault ? 'default' : 'named'}`; const filteredImportInfo = { ...importInfo, importedTypes: filteredTypes, modulePath: resolvedPath }; if (importMap.has(key)) { // Merge imported types const existing = importMap.get(key); const mergedTypes = [...new Set([...existing.importedTypes, ...filteredImportInfo.importedTypes])]; importMap.set(key, { ...existing, importedTypes: mergedTypes }); } else { importMap.set(key, filteredImportInfo); } } async writeTypesFile() { const typesDir = path.join(this.rootPath, '.verve'); const typesFile = path.join(typesDir, 'models.d.ts'); // Ensure directory exists await fs.promises.mkdir(typesDir, { recursive: true }); let content = `// ------------------------------------------------------ // THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY) // ------------------------------------------------------ `; // Collect and deduplicate imports const allImports = this.collectImports(typesFile); // Add import statements using original syntax if (allImports.length > 0) { for (const importInfo of allImports) { if (importInfo.originalStatement) { // Use the original import statement with resolved path const originalWithResolvedPath = importInfo.originalStatement.replace(/from\s*['"][^'"]+['"]/, `from '${importInfo.modulePath}'`); // Ensure we don't add double semicolons const cleanStatement = originalWithResolvedPath.endsWith(';') ? originalWithResolvedPath : originalWithResolvedPath + ';'; content += cleanStatement + '\n'; } else { // Fallback to reconstructed import (shouldn't happen with new logic) if (importInfo.isDefault) { content += `import ${importInfo.importedTypes[0]} from '${importInfo.modulePath}';\n`; } else if (importInfo.alias) { content += `import * as ${importInfo.alias} from '${importInfo.modulePath}';\n`; } else { content += `import { ${importInfo.importedTypes.join(', ')} } from '${importInfo.modulePath}';\n`; } } } content += '\n'; } for (const model of this.models) { content += `export type ${model.name} = VerveModels['${model.name}'];\n`; } content += '\n'; // Extract and include enum definitions const enumDefinitions = this.extractEnumDefinitions(); if (enumDefinitions.length > 0) { content += enumDefinitions.join('\n') + '\n\n'; } content += `declare global {\n`; content += ` interface VerveModels {\n`; for (const model of this.models) { content += ` ${model.name}: {\n`; // Generate field types for (const field of model.fields) { content += ` ${field.name}: ${field.type};\n`; } content += ` };\n`; } content += ` }\n`; content += `}\n`; content += `\nexport {};\n`; await fs.promises.writeFile(typesFile, content, 'utf-8'); console.log(`Generated types written to: ${typesFile}`); return content; } async updateModelClasses() { for (const filePath of this.processedFiles) { try { let content = await fs.promises.readFile(filePath, 'utf-8'); let modified = false; for (const model of this.models) { // Look for class definitions that extend Model.Typed<any>() const anyPattern = new RegExp(`(class\\s+${model.name}\\s+extends\\s+Model\\.Typed)<any>\\(\\)`, 'g'); if (anyPattern.test(content)) { content = content.replace(anyPattern, `$1<'${model.name}'>()`); modified = true; console.log(`Updated ${model.name} class in ${path.relative(this.rootPath, filePath)}`); } } if (modified) { await fs.promises.writeFile(filePath, content, 'utf-8'); } } catch (error) { console.warn(`Warning: Could not update file ${filePath}:`, error); } } } } exports.ModelTypeGenerator = ModelTypeGenerator; // Programmatic API async function generateTypes(targetPath) { const scanPath = targetPath || process.cwd(); if (!fs.existsSync(scanPath)) { throw new Error(`Path "${scanPath}" does not exist.`); } const generator = new ModelTypeGenerator(scanPath); await generator.generateTypes(); } // CLI handling async function main() { const args = process.argv.slice(2); const targetPath = args[0] || process.cwd(); console.log(`Scanning for models in: ${targetPath}`); try { await generateTypes(targetPath); console.log('Type generation completed successfully!'); } catch (error) { console.error('Error during type generation:', error); process.exit(1); } } if (require.main === module) { main().catch(console.error); } //# sourceMappingURL=generate-types.js.map