@famgia/omnify-laravel 0.0.54 → 0.0.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1224,6 +1224,7 @@ var DEFAULT_OPTIONS = {
1224
1224
  baseModelClassName: "BaseModel",
1225
1225
  baseModelPath: "app/Models/OmnifyBase",
1226
1226
  modelPath: "app/Models",
1227
+ providersPath: "app/Providers",
1227
1228
  customTypes: /* @__PURE__ */ new Map()
1228
1229
  };
1229
1230
  function generateLocalizedDisplayNames(displayName, indent = " ") {
@@ -1272,6 +1273,7 @@ function resolveOptions(options) {
1272
1273
  baseModelClassName: options?.baseModelClassName ?? DEFAULT_OPTIONS.baseModelClassName,
1273
1274
  baseModelPath: options?.baseModelPath ?? DEFAULT_OPTIONS.baseModelPath,
1274
1275
  modelPath: options?.modelPath ?? DEFAULT_OPTIONS.modelPath,
1276
+ providersPath: options?.providersPath ?? DEFAULT_OPTIONS.providersPath,
1275
1277
  customTypes: options?.customTypes ?? /* @__PURE__ */ new Map()
1276
1278
  };
1277
1279
  }
@@ -2106,7 +2108,7 @@ function generateServiceProvider(schemas, options, stubContent) {
2106
2108
  }).join("\n");
2107
2109
  const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
2108
2110
  return {
2109
- path: "app/Providers/OmnifyServiceProvider.php",
2111
+ path: `${options.providersPath}/OmnifyServiceProvider.php`,
2110
2112
  content,
2111
2113
  type: "service-provider",
2112
2114
  overwrite: true,
@@ -2559,6 +2561,14 @@ var LARAVEL_CONFIG_SCHEMA = {
2559
2561
  default: "app/Models/OmnifyBase",
2560
2562
  group: "output"
2561
2563
  },
2564
+ {
2565
+ key: "providersPath",
2566
+ type: "path",
2567
+ label: "Providers Path",
2568
+ description: "Directory for Laravel service provider files",
2569
+ default: "app/Providers",
2570
+ group: "output"
2571
+ },
2562
2572
  {
2563
2573
  key: "generateModels",
2564
2574
  type: "boolean",
@@ -2598,6 +2608,7 @@ function resolveOptions3(options) {
2598
2608
  migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
2599
2609
  modelsPath: options?.modelsPath ?? "app/Models",
2600
2610
  baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
2611
+ providersPath: options?.providersPath ?? "app/Providers",
2601
2612
  modelNamespace: options?.modelNamespace ?? "App\\Models",
2602
2613
  baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
2603
2614
  generateModels: options?.generateModels ?? true,
@@ -2702,6 +2713,7 @@ function laravelPlugin(options) {
2702
2713
  baseModelNamespace: resolved.baseModelNamespace,
2703
2714
  modelPath: resolved.modelsPath,
2704
2715
  baseModelPath: resolved.baseModelsPath,
2716
+ providersPath: resolved.providersPath,
2705
2717
  customTypes: ctx.customTypes
2706
2718
  };
2707
2719
  const models = generateModels(ctx.schemas, modelOptions);
@@ -2820,4 +2832,4 @@ export {
2820
2832
  generateProviderRegistration,
2821
2833
  laravelPlugin
2822
2834
  };
2823
- //# sourceMappingURL=chunk-U7ZCSAYW.js.map
2835
+ //# sourceMappingURL=chunk-TIMQTRAC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/plugin.ts","../src/migration/schema-builder.ts","../src/migration/generator.ts","../src/migration/alter-generator.ts","../src/model/generator.ts","../src/utils.ts","../src/factory/generator.ts"],"sourcesContent":["/**\n * @famgia/omnify-laravel - Plugin\n *\n * Plugin for generating Laravel migration files and Eloquent models from Omnify schemas.\n *\n * @example\n * ```typescript\n * import { defineConfig } from '@famgia/omnify';\n * import laravel from '@famgia/omnify-laravel/plugin';\n *\n * export default defineConfig({\n * plugins: [\n * laravel({\n * migrationsPath: 'database/migrations',\n * modelsPath: 'app/Models',\n * connection: 'mysql',\n * }),\n * ],\n * });\n * ```\n */\n\nimport { readFileSync, existsSync, readdirSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { OmnifyPlugin, GeneratorOutput, GeneratorContext, PluginConfigSchema, SchemaChange } from '@famgia/omnify-types';\nimport { generateMigrations, getMigrationPath, generateMigrationsFromChanges, type MigrationOptions } from './migration/index.js';\nimport { generateModels, getModelPath, generateProviderRegistration, type ModelGeneratorOptions } from './model/index.js';\nimport { generateFactories, getFactoryPath, type FactoryGeneratorOptions } from './factory/index.js';\n\n/**\n * Scans a directory for existing migration files and returns tables that already have CREATE migrations.\n */\nfunction getExistingMigrationTables(migrationsDir: string): Set<string> {\n const existingTables = new Set<string>();\n\n if (!existsSync(migrationsDir)) {\n return existingTables;\n }\n\n try {\n const files = readdirSync(migrationsDir);\n // Match pattern: YYYY_MM_DD_HHMMSS_create_<table>_table.php\n const createMigrationPattern = /^\\d{4}_\\d{2}_\\d{2}_\\d{6}_create_(.+)_table\\.php$/;\n\n for (const file of files) {\n const match = file.match(createMigrationPattern);\n if (match) {\n existingTables.add(match[1]); // table name\n }\n }\n } catch {\n // Ignore errors reading directory\n }\n\n return existingTables;\n}\n\n/**\n * Configuration schema for Laravel plugin UI settings\n */\nconst LARAVEL_CONFIG_SCHEMA: PluginConfigSchema = {\n fields: [\n {\n key: 'migrationsPath',\n type: 'path',\n label: 'Migrations Path',\n description: 'Directory for Laravel migration files (loaded via OmnifyServiceProvider)',\n default: 'database/migrations/omnify',\n group: 'output',\n },\n {\n key: 'modelsPath',\n type: 'path',\n label: 'Models Path',\n description: 'Directory for user-editable model files',\n default: 'app/Models',\n group: 'output',\n },\n {\n key: 'baseModelsPath',\n type: 'path',\n label: 'Base Models Path',\n description: 'Directory for auto-generated base model files',\n default: 'app/Models/OmnifyBase',\n group: 'output',\n },\n {\n key: 'providersPath',\n type: 'path',\n label: 'Providers Path',\n description: 'Directory for Laravel service provider files',\n default: 'app/Providers',\n group: 'output',\n },\n {\n key: 'generateModels',\n type: 'boolean',\n label: 'Generate Models',\n description: 'Generate Eloquent model classes',\n default: true,\n group: 'options',\n },\n {\n key: 'factoriesPath',\n type: 'path',\n label: 'Factories Path',\n description: 'Directory for Laravel factory files',\n default: 'database/factories',\n group: 'output',\n },\n {\n key: 'generateFactories',\n type: 'boolean',\n label: 'Generate Factories',\n description: 'Generate Laravel factory classes for testing',\n default: true,\n group: 'options',\n },\n {\n key: 'connection',\n type: 'string',\n label: 'Database Connection',\n description: 'Laravel database connection name (optional)',\n placeholder: 'mysql',\n group: 'options',\n },\n ],\n};\n\n/**\n * Options for the Laravel plugin.\n */\nexport interface LaravelPluginOptions {\n /**\n * Path for Laravel migration files.\n * @default 'database/migrations/omnify'\n */\n migrationsPath?: string;\n\n /**\n * Path for user-editable model files.\n * @default 'app/Models'\n */\n modelsPath?: string;\n\n /**\n * Path for auto-generated base model files.\n * @default 'app/Models/OmnifyBase'\n */\n baseModelsPath?: string;\n\n /**\n * Path for Laravel service provider files.\n * @default 'app/Providers'\n */\n providersPath?: string;\n\n /**\n * Model namespace.\n * @default 'App\\\\Models'\n */\n modelNamespace?: string;\n\n /**\n * Base model namespace.\n * @default 'App\\\\Models\\\\OmnifyBase'\n */\n baseModelNamespace?: string;\n\n /**\n * Whether to generate Eloquent models.\n * @default true\n */\n generateModels?: boolean;\n\n /**\n * Path for Laravel factory files.\n * @default 'database/factories'\n */\n factoriesPath?: string;\n\n /**\n * Whether to generate Laravel factories.\n * @default true\n */\n generateFactories?: boolean;\n\n /**\n * Faker locale for factory data.\n * @default 'en_US'\n */\n fakerLocale?: string;\n\n /**\n * Database connection name for migrations.\n */\n connection?: string;\n\n /**\n * Custom timestamp for migration file names (mainly for testing).\n */\n timestamp?: string;\n}\n\n/**\n * Resolved options with defaults applied.\n */\ninterface ResolvedOptions {\n migrationsPath: string;\n modelsPath: string;\n baseModelsPath: string;\n providersPath: string;\n modelNamespace: string;\n baseModelNamespace: string;\n generateModels: boolean;\n factoriesPath: string;\n generateFactories: boolean;\n fakerLocale: string;\n connection: string | undefined;\n timestamp: string | undefined;\n}\n\n/**\n * Resolves options with defaults.\n */\nfunction resolveOptions(options?: LaravelPluginOptions): ResolvedOptions {\n return {\n migrationsPath: options?.migrationsPath ?? 'database/migrations/omnify',\n modelsPath: options?.modelsPath ?? 'app/Models',\n baseModelsPath: options?.baseModelsPath ?? 'app/Models/OmnifyBase',\n providersPath: options?.providersPath ?? 'app/Providers',\n modelNamespace: options?.modelNamespace ?? 'App\\\\Models',\n baseModelNamespace: options?.baseModelNamespace ?? 'App\\\\Models\\\\OmnifyBase',\n generateModels: options?.generateModels ?? true,\n factoriesPath: options?.factoriesPath ?? 'database/factories',\n generateFactories: options?.generateFactories ?? true,\n fakerLocale: options?.fakerLocale ?? 'en_US',\n connection: options?.connection,\n timestamp: options?.timestamp,\n };\n}\n\n/**\n * Creates the Laravel plugin with the specified options.\n *\n * @param options - Plugin configuration options\n * @returns OmnifyPlugin configured for Laravel migrations and models\n */\nexport default function laravelPlugin(options?: LaravelPluginOptions): OmnifyPlugin {\n const resolved = resolveOptions(options);\n\n // Build generators array\n const migrationGenerator = {\n name: 'laravel-migrations',\n description: 'Generate Laravel migration files',\n\n generate: async (ctx: GeneratorContext): Promise<GeneratorOutput[]> => {\n const migrationOptions: MigrationOptions = {\n connection: resolved.connection,\n timestamp: resolved.timestamp,\n customTypes: ctx.customTypes,\n };\n\n const outputs: GeneratorOutput[] = [];\n const migrationsDir = join(ctx.cwd, resolved.migrationsPath);\n const existingTables = getExistingMigrationTables(migrationsDir);\n\n // If we have change information (including empty array), use it for smarter migration generation\n // undefined = no change info → fallback to generating all\n // empty array = no changes detected → no migrations needed\n if (ctx.changes !== undefined) {\n // Empty changes array = no changes, no migrations needed\n if (ctx.changes.length === 0) {\n return outputs;\n }\n\n // Generate CREATE migrations only for added schemas\n const addedSchemaNames = new Set(\n ctx.changes\n .filter((c) => c.changeType === 'added')\n .map((c) => c.schemaName)\n );\n\n if (addedSchemaNames.size > 0) {\n const addedSchemas = Object.fromEntries(\n Object.entries(ctx.schemas).filter(([name]) => addedSchemaNames.has(name))\n );\n\n const createMigrations = generateMigrations(addedSchemas, migrationOptions);\n\n for (const migration of createMigrations) {\n const tableName = migration.tables[0];\n // Skip if table already has a create migration\n if (existingTables.has(tableName)) {\n ctx.logger.debug(`Skipping CREATE for ${tableName} (already exists)`);\n continue;\n }\n\n outputs.push({\n path: getMigrationPath(migration, resolved.migrationsPath),\n content: migration.content,\n type: 'migration' as const,\n metadata: {\n tableName,\n migrationType: 'create',\n },\n });\n }\n }\n\n // Generate ALTER/DROP migrations for modified/removed schemas\n const alterChanges = ctx.changes.filter(\n (c) => c.changeType === 'modified' || c.changeType === 'removed'\n );\n\n if (alterChanges.length > 0) {\n // Convert SchemaChange to the format expected by alter-generator\n const alterMigrations = generateMigrationsFromChanges(\n alterChanges as unknown as import('@famgia/omnify-atlas').SchemaChange[],\n migrationOptions\n );\n\n for (const migration of alterMigrations) {\n outputs.push({\n path: getMigrationPath(migration, resolved.migrationsPath),\n content: migration.content,\n type: 'migration' as const,\n metadata: {\n tableName: migration.tables[0],\n migrationType: migration.type,\n },\n });\n }\n }\n } else {\n // No change info - generate CREATE migrations for all schemas\n // but skip tables that already have migrations (deduplication)\n const migrations = generateMigrations(ctx.schemas, migrationOptions);\n\n for (const migration of migrations) {\n const tableName = migration.tables[0];\n if (migration.type === 'create' && existingTables.has(tableName)) {\n ctx.logger.debug(`Skipping migration for ${tableName} (already exists)`);\n continue;\n }\n\n outputs.push({\n path: getMigrationPath(migration, resolved.migrationsPath),\n content: migration.content,\n type: 'migration' as const,\n metadata: {\n tableName,\n migrationType: migration.type,\n },\n });\n }\n }\n\n return outputs;\n },\n };\n\n const modelGenerator = {\n name: 'laravel-models',\n description: 'Generate Eloquent model classes',\n\n generate: async (ctx: GeneratorContext): Promise<GeneratorOutput[]> => {\n const modelOptions: ModelGeneratorOptions = {\n modelNamespace: resolved.modelNamespace,\n baseModelNamespace: resolved.baseModelNamespace,\n modelPath: resolved.modelsPath,\n baseModelPath: resolved.baseModelsPath,\n providersPath: resolved.providersPath,\n customTypes: ctx.customTypes,\n };\n\n const models = generateModels(ctx.schemas, modelOptions);\n const outputs: GeneratorOutput[] = models.map((model) => ({\n path: getModelPath(model),\n content: model.content,\n type: 'model' as const,\n // Skip writing user models if they already exist\n skipIfExists: !model.overwrite,\n metadata: {\n modelType: model.type,\n schemaName: model.schemaName,\n },\n }));\n\n // Generate provider registration\n // Check for Laravel 11+ (bootstrap/providers.php) or Laravel 10- (config/app.php)\n const bootstrapProvidersPath = join(ctx.cwd, 'bootstrap/providers.php');\n const configAppPath = join(ctx.cwd, 'config/app.php');\n\n let existingContent: string | null = null;\n let laravelVersion: 'laravel11+' | 'laravel10-';\n\n if (existsSync(bootstrapProvidersPath)) {\n // Laravel 11+\n laravelVersion = 'laravel11+';\n try {\n existingContent = readFileSync(bootstrapProvidersPath, 'utf-8');\n } catch {\n existingContent = null;\n }\n } else if (existsSync(configAppPath)) {\n // Laravel 10-\n laravelVersion = 'laravel10-';\n try {\n existingContent = readFileSync(configAppPath, 'utf-8');\n } catch {\n existingContent = null;\n }\n } else {\n // Assume Laravel 11+ for new projects\n laravelVersion = 'laravel11+';\n }\n\n const registration = generateProviderRegistration(existingContent, laravelVersion);\n\n if (registration && !registration.alreadyRegistered) {\n outputs.push({\n path: registration.path,\n content: registration.content,\n type: 'other' as const,\n skipIfExists: false, // We want to modify the file\n metadata: {\n registrationType: 'provider-registration',\n laravelVersion: registration.laravelVersion,\n },\n });\n ctx.logger.info(`OmnifyServiceProvider will be registered in ${registration.path}`);\n } else if (registration?.alreadyRegistered) {\n ctx.logger.info('OmnifyServiceProvider is already registered');\n }\n\n return outputs;\n },\n };\n\n const factoryGenerator = {\n name: 'laravel-factories',\n description: 'Generate Laravel factory classes for testing',\n\n generate: async (ctx: GeneratorContext): Promise<GeneratorOutput[]> => {\n const factoryOptions: FactoryGeneratorOptions = {\n modelNamespace: resolved.modelNamespace,\n factoryPath: resolved.factoriesPath,\n fakerLocale: resolved.fakerLocale,\n };\n\n const factories = generateFactories(ctx.schemas, factoryOptions);\n\n return factories.map((factory) => ({\n path: getFactoryPath(factory),\n content: factory.content,\n type: 'factory' as const,\n // Skip writing factories if they already exist (allow customization)\n skipIfExists: !factory.overwrite,\n metadata: {\n factoryName: factory.name,\n schemaName: factory.schemaName,\n },\n }));\n },\n };\n\n // Build generators array based on options\n const generators = [migrationGenerator];\n if (resolved.generateModels) {\n generators.push(modelGenerator);\n }\n if (resolved.generateFactories) {\n generators.push(factoryGenerator);\n }\n\n return {\n name: '@famgia/omnify-laravel',\n version: '0.0.14',\n configSchema: LARAVEL_CONFIG_SCHEMA,\n generators,\n };\n}\n\n// Named export for flexibility\nexport { laravelPlugin };\n","/**\n * @famgia/omnify-laravel - Schema Builder Converter\n *\n * Converts SQL types and operations to Laravel Schema Builder methods.\n */\n\nimport type { PropertyDefinition, LoadedSchema, SchemaCollection, CustomTypeDefinition, LocalizedString, LocaleResolutionOptions } from '@famgia/omnify-types';\nimport { resolveLocalizedString } from '@famgia/omnify-types';\nimport type {\n ColumnMethod,\n ColumnModifier,\n ForeignKeyDefinition,\n IndexDefinition,\n TableBlueprint,\n} from './types.js';\n\n/**\n * Maps Omnify property types to Laravel column methods.\n */\nconst TYPE_METHOD_MAP: Record<string, string> = {\n String: 'string',\n TinyInt: 'tinyInteger',\n Int: 'integer',\n BigInt: 'bigInteger',\n Float: 'double',\n Decimal: 'decimal',\n Boolean: 'boolean',\n Text: 'text',\n MediumText: 'mediumText',\n LongText: 'longText',\n Date: 'date',\n Time: 'time',\n DateTime: 'dateTime',\n Timestamp: 'timestamp',\n Json: 'json',\n Email: 'string',\n Password: 'string',\n Enum: 'enum',\n EnumRef: 'string', // EnumRef stores the enum value as string (lookup via schema)\n // Note: File type is now polymorphic - no column generated, uses files table\n};\n\n/**\n * Maps primary key types to Laravel methods.\n * Laravel 8+ uses id() for BigInt auto-increment primary keys.\n */\nconst PK_METHOD_MAP: Record<string, string> = {\n Int: 'increments',\n BigInt: 'id',\n Uuid: 'uuid',\n String: 'string',\n};\n\n/**\n * Gets the ID type from schema options.\n */\nfunction getIdType(schema: LoadedSchema): 'Int' | 'BigInt' | 'Uuid' | 'String' {\n return (schema.options?.idType ?? 'BigInt') as 'Int' | 'BigInt' | 'Uuid' | 'String';\n}\n\n/**\n * Checks if the schema should have an auto-generated ID column.\n * Returns false if options.id is explicitly set to false.\n */\nfunction hasAutoId(schema: LoadedSchema): boolean {\n return schema.options?.id !== false;\n}\n\n/**\n * Converts a property name to snake_case column name.\n */\nexport function toColumnName(propertyName: string): string {\n return propertyName.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '');\n}\n\n/**\n * Converts schema name to snake_case plural table name.\n */\nexport function toTableName(schemaName: string): string {\n const snakeCase = schemaName\n .replace(/([A-Z])/g, '_$1')\n .toLowerCase()\n .replace(/^_/, '');\n\n // Simple pluralization\n if (snakeCase.endsWith('y')) {\n return snakeCase.slice(0, -1) + 'ies';\n } else if (\n snakeCase.endsWith('s') ||\n snakeCase.endsWith('x') ||\n snakeCase.endsWith('ch') ||\n snakeCase.endsWith('sh')\n ) {\n return snakeCase + 'es';\n } else {\n return snakeCase + 's';\n }\n}\n\n/**\n * Options for property to column conversion.\n */\nexport interface PropertyToColumnOptions {\n /** Locale resolution options for displayName */\n locale?: LocaleResolutionOptions;\n}\n\n/**\n * Converts a property to Laravel column method.\n */\nexport function propertyToColumnMethod(\n propertyName: string,\n property: PropertyDefinition,\n options: PropertyToColumnOptions = {}\n): ColumnMethod | null {\n // Skip associations - they're handled separately\n if (property.type === 'Association') {\n return null;\n }\n\n // Skip File type - uses polymorphic relation to files table\n if (property.type === 'File') {\n return null;\n }\n\n const columnName = toColumnName(propertyName);\n const method = TYPE_METHOD_MAP[property.type] ?? 'string';\n const args: (string | number | boolean)[] = [columnName];\n const modifiers: ColumnModifier[] = [];\n\n // Handle length for string types\n const propWithLength = property as { length?: number };\n if (method === 'string' && propWithLength.length) {\n args.push(propWithLength.length);\n } else if (property.type === 'EnumRef') {\n // Default length for EnumRef columns (store enum value, typically short codes)\n args.push(50);\n }\n\n // Handle precision and scale for decimal types\n if (property.type === 'Decimal') {\n const decimalProp = property as { precision?: number; scale?: number };\n const precision = decimalProp.precision ?? 8;\n const scale = decimalProp.scale ?? 2;\n args.push(precision, scale);\n }\n\n // Handle enum values\n if (property.type === 'Enum') {\n const enumProp = property as { enum?: readonly string[] };\n if (enumProp.enum && enumProp.enum.length > 0) {\n args.push(enumProp.enum as unknown as string);\n }\n }\n\n // Add modifiers\n const baseProp = property as {\n nullable?: boolean;\n unique?: boolean;\n default?: unknown;\n unsigned?: boolean;\n primary?: boolean;\n };\n\n // Add primary key modifier (for custom primary keys with id: false)\n if (baseProp.primary) {\n modifiers.push({ method: 'primary' });\n }\n\n if (baseProp.nullable) {\n modifiers.push({ method: 'nullable' });\n }\n\n if (baseProp.unique) {\n modifiers.push({ method: 'unique' });\n }\n\n if (baseProp.default !== undefined && baseProp.default !== null) {\n // Keep native types for proper PHP rendering (boolean false vs string 'false')\n modifiers.push({ method: 'default', args: [baseProp.default as string | number | boolean] });\n }\n\n if (baseProp.unsigned && (method === 'tinyInteger' || method === 'integer' || method === 'bigInteger')) {\n modifiers.push({ method: 'unsigned' });\n }\n\n // Timestamp modifiers: useCurrent and useCurrentOnUpdate\n if (method === 'timestamp') {\n const timestampProp = property as { useCurrent?: boolean; useCurrentOnUpdate?: boolean };\n if (timestampProp.useCurrent) {\n modifiers.push({ method: 'useCurrent' });\n }\n if (timestampProp.useCurrentOnUpdate) {\n modifiers.push({ method: 'useCurrentOnUpdate' });\n }\n }\n\n // Add comment from displayName if available (resolve LocalizedString to string)\n const rawDisplayName = (property as { displayName?: LocalizedString }).displayName;\n if (rawDisplayName) {\n const displayName = resolveLocalizedString(rawDisplayName, options.locale);\n if (displayName) {\n modifiers.push({ method: 'comment', args: [displayName] });\n }\n }\n\n return {\n name: columnName,\n method,\n args,\n modifiers,\n };\n}\n\n/**\n * Generates primary key column method.\n */\nexport function generatePrimaryKeyColumn(\n pkType: 'Int' | 'BigInt' | 'Uuid' | 'String' = 'BigInt'\n): ColumnMethod {\n const method = PK_METHOD_MAP[pkType] ?? 'id';\n\n if (pkType === 'Uuid') {\n return {\n name: 'id',\n method: 'uuid',\n args: ['id'],\n modifiers: [{ method: 'primary' }],\n };\n }\n\n if (pkType === 'String') {\n return {\n name: 'id',\n method: 'string',\n args: ['id', 255],\n modifiers: [{ method: 'primary' }],\n };\n }\n\n // For Int/BigInt, use increments/id which auto-creates primary key\n // $table->id() needs no args, $table->increments('id') needs column name\n return {\n name: 'id',\n method,\n args: method === 'id' ? [] : ['id'],\n modifiers: [],\n };\n}\n\n/**\n * Generates timestamp columns.\n */\nexport function generateTimestampColumns(): ColumnMethod[] {\n return [\n {\n name: 'created_at',\n method: 'timestamp',\n args: ['created_at'],\n modifiers: [{ method: 'nullable' }],\n },\n {\n name: 'updated_at',\n method: 'timestamp',\n args: ['updated_at'],\n modifiers: [{ method: 'nullable' }],\n },\n ];\n}\n\n/**\n * Generates soft delete column.\n */\nexport function generateSoftDeleteColumn(): ColumnMethod {\n return {\n name: 'deleted_at',\n method: 'timestamp',\n args: ['deleted_at'],\n modifiers: [{ method: 'nullable' }],\n };\n}\n\n/**\n * Polymorphic column result with type enum and id column.\n */\nexport interface PolymorphicColumnsResult {\n typeColumn: ColumnMethod;\n idColumn: ColumnMethod;\n indexes: IndexDefinition[];\n}\n\n/**\n * Generates polymorphic columns for MorphTo relations.\n * Creates {name}_type (ENUM) and {name}_id columns.\n */\nexport function generatePolymorphicColumns(\n propertyName: string,\n property: PropertyDefinition,\n allSchemas: SchemaCollection\n): PolymorphicColumnsResult | null {\n if (property.type !== 'Association') {\n return null;\n }\n\n const assocProp = property as {\n relation?: string;\n targets?: readonly string[];\n nullable?: boolean;\n };\n\n // Only handle MorphTo - it creates the type and id columns\n if (assocProp.relation !== 'MorphTo') {\n return null;\n }\n\n // Check if nullable is explicitly set (default: true for MorphTo)\n const isNullable = assocProp.nullable !== false;\n\n const targets = assocProp.targets;\n if (!targets || targets.length === 0) {\n return null;\n }\n\n const columnBaseName = toColumnName(propertyName);\n const typeColumnName = `${columnBaseName}_type`;\n const idColumnName = `${columnBaseName}_id`;\n\n // Generate ENUM type column with target schema names as values\n const typeColumn: ColumnMethod = {\n name: typeColumnName,\n method: 'enum',\n args: [typeColumnName, targets as unknown as string],\n modifiers: isNullable ? [{ method: 'nullable' }] : [],\n };\n\n // For polymorphic id, we need to determine the largest ID type among targets\n // Default to unsignedBigInteger for maximum compatibility\n let idMethod = 'unsignedBigInteger';\n\n // Check if any target uses UUID or String - those take precedence\n for (const targetName of targets) {\n const targetSchema = allSchemas[targetName];\n if (targetSchema) {\n const targetIdType = (targetSchema.options?.idType ?? 'BigInt') as string;\n if (targetIdType === 'Uuid') {\n idMethod = 'uuid';\n break; // UUID takes highest precedence\n } else if (targetIdType === 'String') {\n idMethod = 'string';\n // Don't break - UUID still takes precedence\n }\n }\n }\n\n const idColumn: ColumnMethod = {\n name: idColumnName,\n method: idMethod,\n args: idMethod === 'string' ? [idColumnName, 255] : [idColumnName],\n modifiers: isNullable ? [{ method: 'nullable' }] : [],\n };\n\n // Create composite index for faster polymorphic lookups\n const indexes: IndexDefinition[] = [\n {\n columns: [typeColumnName, idColumnName],\n unique: false,\n },\n ];\n\n return { typeColumn, idColumn, indexes };\n}\n\n/**\n * Generates foreign key column and constraint from association.\n */\nexport function generateForeignKey(\n propertyName: string,\n property: PropertyDefinition,\n allSchemas: SchemaCollection,\n options: PropertyToColumnOptions = {}\n): { column: ColumnMethod; foreignKey: ForeignKeyDefinition; index: IndexDefinition } | null {\n if (property.type !== 'Association') {\n return null;\n }\n\n const assocProp = property as {\n relation?: string;\n target?: string;\n onDelete?: string;\n onUpdate?: string;\n mappedBy?: string;\n owningSide?: boolean;\n nullable?: boolean;\n default?: string | number;\n displayName?: LocalizedString;\n };\n\n // Only create FK column for ManyToOne and OneToOne (owning side)\n if (assocProp.relation !== 'ManyToOne' && assocProp.relation !== 'OneToOne') {\n return null;\n }\n\n // Skip inverse side (mappedBy means this is the inverse side)\n if (assocProp.mappedBy) {\n return null;\n }\n\n const columnName = toColumnName(propertyName) + '_id';\n const targetSchema = assocProp.target ? allSchemas[assocProp.target] : undefined;\n const targetTable = assocProp.target ? toTableName(assocProp.target) : 'unknown';\n const targetPkType = targetSchema ? getIdType(targetSchema) : 'BigInt';\n\n // Determine column method based on target PK type\n let method = 'unsignedBigInteger';\n if (targetPkType === 'Int') {\n method = 'unsignedInteger';\n } else if (targetPkType === 'Uuid') {\n method = 'uuid';\n } else if (targetPkType === 'String') {\n method = 'string';\n }\n\n // Build modifiers for the column\n const modifiers: ColumnModifier[] = [];\n\n // Add nullable only if explicitly set to true (consistent with other property types)\n if (assocProp.nullable === true) {\n modifiers.push({ method: 'nullable' });\n }\n\n // Add default if specified\n if (assocProp.default !== undefined && assocProp.default !== null) {\n modifiers.push({ method: 'default', args: [assocProp.default] });\n }\n\n // Add comment from displayName if available (resolve LocalizedString to string)\n if (assocProp.displayName) {\n const displayName = resolveLocalizedString(assocProp.displayName, options.locale);\n if (displayName) {\n modifiers.push({ method: 'comment', args: [displayName] });\n }\n }\n\n const column: ColumnMethod = {\n name: columnName,\n method,\n args: [columnName],\n modifiers,\n };\n\n const foreignKey: ForeignKeyDefinition = {\n columns: [columnName],\n references: 'id',\n on: [targetTable],\n onDelete: assocProp.onDelete ?? 'restrict',\n onUpdate: assocProp.onUpdate ?? 'cascade',\n };\n\n // Don't specify index name - let Laravel auto-generate unique names\n const index: IndexDefinition = {\n columns: [columnName],\n unique: false,\n };\n\n return { column, foreignKey, index };\n}\n\n/**\n * Expands compound type properties into multiple columns.\n * Returns the expanded properties or null if not a compound type.\n */\nfunction expandCompoundType(\n propName: string,\n property: PropertyDefinition,\n customTypes: ReadonlyMap<string, CustomTypeDefinition>,\n options: PropertyToColumnOptions = {}\n): { name: string; property: PropertyDefinition }[] | null {\n const typeDef = customTypes.get(property.type);\n\n if (!typeDef || !typeDef.compound || !typeDef.expand) {\n return null;\n }\n\n const expanded: { name: string; property: PropertyDefinition }[] = [];\n const baseProp = property as unknown as Record<string, unknown>;\n\n for (const field of typeDef.expand) {\n // Generate column name: propName + suffix (converted to snake_case)\n const suffixSnake = toColumnName(field.suffix);\n const columnName = `${propName}_${suffixSnake}`;\n\n // Build property definition\n const expandedProp: Record<string, unknown> = {\n type: 'String', // Default type, will be overridden by sql definition\n };\n\n // Check for per-field overrides from schema's `fields` property\n const fieldOverrides = baseProp.fields as Record<string, { nullable?: boolean; hidden?: boolean; fillable?: boolean; length?: number }> | undefined;\n const fieldOverride = fieldOverrides?.[field.suffix];\n\n // Handle enumRef field - reference to enum schema\n const fieldWithEnumRef = field as { enumRef?: string };\n if (fieldWithEnumRef.enumRef) {\n expandedProp.type = 'EnumRef';\n expandedProp.enum = fieldWithEnumRef.enumRef;\n // Inherit nullable from parent property, or use per-field override\n if (fieldOverride?.nullable !== undefined) {\n expandedProp.nullable = fieldOverride.nullable;\n } else if (baseProp.nullable !== undefined) {\n expandedProp.nullable = baseProp.nullable;\n }\n }\n // Map SQL type to Omnify type\n else if (field.sql) {\n const sqlType = field.sql.sqlType.toUpperCase();\n if (sqlType === 'VARCHAR' || sqlType === 'CHAR' || sqlType === 'STRING') {\n expandedProp.type = 'String';\n // Use field override length if provided, otherwise use default from type definition\n if (fieldOverride?.length) {\n expandedProp.length = fieldOverride.length;\n } else if (field.sql.length) {\n expandedProp.length = field.sql.length;\n }\n } else if (sqlType === 'TINYINT') {\n expandedProp.type = 'TinyInt';\n } else if (sqlType === 'INT' || sqlType === 'INTEGER') {\n expandedProp.type = 'Int';\n } else if (sqlType === 'BIGINT') {\n expandedProp.type = 'BigInt';\n } else if (sqlType === 'TEXT') {\n expandedProp.type = 'Text';\n } else if (sqlType === 'BOOLEAN' || sqlType === 'BOOL') {\n expandedProp.type = 'Boolean';\n } else if (sqlType === 'DECIMAL') {\n expandedProp.type = 'Decimal';\n if (field.sql.precision) expandedProp.precision = field.sql.precision;\n if (field.sql.scale) expandedProp.scale = field.sql.scale;\n } else if (sqlType === 'DATE') {\n expandedProp.type = 'Date';\n } else if (sqlType === 'TIMESTAMP' || sqlType === 'DATETIME') {\n expandedProp.type = 'Timestamp';\n }\n\n // Handle unsigned flag for integer types\n if (field.sql.unsigned) {\n expandedProp.unsigned = true;\n }\n\n // Handle default value\n if (field.sql.default !== undefined) {\n expandedProp.default = field.sql.default;\n }\n\n // Handle nullable: priority is per-field override > field.sql.nullable > parent property\n if (field.sql.nullable !== undefined) {\n expandedProp.nullable = field.sql.nullable;\n } else if (baseProp.nullable !== undefined) {\n expandedProp.nullable = baseProp.nullable;\n }\n // Per-field nullable override takes highest priority\n if (fieldOverride?.nullable !== undefined) {\n expandedProp.nullable = fieldOverride.nullable;\n }\n }\n\n // Copy displayName if parent has it (with suffix context)\n // Resolve LocalizedString to string before appending suffix\n if (baseProp.displayName) {\n const resolvedDisplayName = resolveLocalizedString(\n baseProp.displayName as LocalizedString,\n options.locale\n );\n if (resolvedDisplayName) {\n expandedProp.displayName = `${resolvedDisplayName} (${field.suffix})`;\n }\n }\n\n expanded.push({\n name: columnName,\n property: expandedProp as unknown as PropertyDefinition,\n });\n }\n\n return expanded;\n}\n\n/**\n * Options for schema to blueprint conversion.\n */\nexport interface SchemaToBlueprintOptions {\n /** Custom types from plugins (for compound type expansion) */\n customTypes?: ReadonlyMap<string, CustomTypeDefinition>;\n /** Locale resolution options for displayName */\n locale?: LocaleResolutionOptions;\n}\n\n/**\n * Generates table blueprint from schema.\n */\nexport function schemaToBlueprint(\n schema: LoadedSchema,\n allSchemas: SchemaCollection,\n options: SchemaToBlueprintOptions = {}\n): TableBlueprint {\n const { customTypes = new Map(), locale } = options;\n const columnOptions: PropertyToColumnOptions = { locale };\n const tableName = toTableName(schema.name);\n const columns: ColumnMethod[] = [];\n const foreignKeys: ForeignKeyDefinition[] = [];\n const indexes: IndexDefinition[] = [];\n\n // Primary key (only if id is not disabled)\n if (hasAutoId(schema)) {\n const pkType = getIdType(schema);\n columns.push(generatePrimaryKeyColumn(pkType));\n }\n\n // Process properties\n if (schema.properties) {\n for (const [propName, property] of Object.entries(schema.properties)) {\n // Check for compound type expansion first\n const expandedProps = expandCompoundType(propName, property, customTypes, columnOptions);\n if (expandedProps) {\n // Compound type - process each expanded property\n for (const { name: expandedName, property: expandedProp } of expandedProps) {\n const columnMethod = propertyToColumnMethod(expandedName, expandedProp, columnOptions);\n if (columnMethod) {\n columns.push(columnMethod);\n }\n }\n continue; // Skip normal processing for compound types\n }\n\n // Handle regular columns\n const columnMethod = propertyToColumnMethod(propName, property, columnOptions);\n if (columnMethod) {\n columns.push(columnMethod);\n }\n\n // Handle foreign keys (standard associations)\n const fkResult = generateForeignKey(propName, property, allSchemas, columnOptions);\n if (fkResult) {\n columns.push(fkResult.column);\n foreignKeys.push(fkResult.foreignKey);\n indexes.push(fkResult.index);\n }\n\n // Handle polymorphic columns (MorphTo)\n const polyResult = generatePolymorphicColumns(propName, property, allSchemas);\n if (polyResult) {\n columns.push(polyResult.typeColumn);\n columns.push(polyResult.idColumn);\n indexes.push(...polyResult.indexes);\n }\n }\n }\n\n // Timestamps\n if (schema.options?.timestamps !== false) {\n columns.push(...generateTimestampColumns());\n }\n\n // Soft delete\n if (schema.options?.softDelete) {\n columns.push(generateSoftDeleteColumn());\n }\n\n // Custom indexes\n if (schema.options?.indexes) {\n // Helper to convert property name to column name, considering Association type\n const propToColName = (propName: string): string => {\n const colName = toColumnName(propName);\n const prop = schema.properties?.[propName];\n if (prop?.type === 'Association') {\n const assoc = prop as { relation?: string; mappedBy?: string };\n // Only add _id for owning side (ManyToOne, OneToOne without mappedBy)\n if (\n (assoc.relation === 'ManyToOne' || assoc.relation === 'OneToOne') &&\n !assoc.mappedBy\n ) {\n return colName + '_id';\n }\n }\n return colName;\n };\n\n for (const index of schema.options.indexes) {\n // Handle both shorthand (string) and full object format\n if (typeof index === 'string') {\n // Shorthand: just column name\n indexes.push({\n columns: [propToColName(index)],\n unique: false,\n });\n } else {\n // Full object format\n indexes.push({\n name: index.name,\n columns: index.columns.map(propToColName),\n unique: index.unique ?? false,\n });\n }\n }\n }\n\n // Unique constraints\n if (schema.options?.unique) {\n // Helper to convert property name to column name, considering Association type\n const propToColName = (propName: string): string => {\n const colName = toColumnName(propName);\n const prop = schema.properties?.[propName];\n if (prop?.type === 'Association') {\n const assoc = prop as { relation?: string; mappedBy?: string };\n if (\n (assoc.relation === 'ManyToOne' || assoc.relation === 'OneToOne') &&\n !assoc.mappedBy\n ) {\n return colName + '_id';\n }\n }\n return colName;\n };\n\n const uniqueConstraints = Array.isArray(schema.options.unique[0])\n ? (schema.options.unique as readonly (readonly string[])[])\n : [schema.options.unique as readonly string[]];\n\n for (const constraint of uniqueConstraints) {\n indexes.push({\n columns: constraint.map(propToColName),\n unique: true,\n });\n }\n }\n\n // Deduplicate indexes by columns (keep first occurrence)\n const seenIndexes = new Set<string>();\n const uniqueIndexes = indexes.filter(idx => {\n const key = idx.columns.join(',') + (idx.unique ? ':unique' : '');\n if (seenIndexes.has(key)) {\n return false;\n }\n seenIndexes.add(key);\n return true;\n });\n\n // Determine primary key column(s)\n let primaryKey: string[] | undefined;\n if (hasAutoId(schema)) {\n primaryKey = ['id'];\n } else if (schema.properties) {\n // Find properties marked with primary: true\n const pkColumns: string[] = [];\n for (const [propName, property] of Object.entries(schema.properties)) {\n if ((property as { primary?: boolean }).primary) {\n pkColumns.push(toColumnName(propName));\n }\n }\n if (pkColumns.length > 0) {\n primaryKey = pkColumns;\n }\n }\n\n return {\n tableName,\n columns,\n primaryKey,\n foreignKeys,\n indexes: uniqueIndexes,\n };\n}\n\n/**\n * Formats a column method to PHP code.\n */\nexport function formatColumnMethod(column: ColumnMethod): string {\n const args = column.args.map(arg => {\n if (typeof arg === 'string') {\n return `'${arg}'`;\n }\n if (Array.isArray(arg)) {\n return `[${(arg as string[]).map(v => `'${v}'`).join(', ')}]`;\n }\n return String(arg);\n }).join(', ');\n\n let code = `$table->${column.method}(${args})`;\n\n for (const modifier of column.modifiers) {\n if (modifier.args && modifier.args.length > 0) {\n const modArgs = modifier.args.map(arg => {\n if (typeof arg === 'string') {\n return `'${arg}'`;\n }\n if (typeof arg === 'boolean') {\n return arg ? 'true' : 'false';\n }\n if (typeof arg === 'number') {\n return String(arg);\n }\n return String(arg);\n }).join(', ');\n code += `->${modifier.method}(${modArgs})`;\n } else {\n code += `->${modifier.method}()`;\n }\n }\n\n return code + ';';\n}\n\n/**\n * Formats a foreign key to PHP code.\n */\nexport function formatForeignKey(fk: ForeignKeyDefinition): string {\n const column = fk.columns[0];\n const table = fk.on[0];\n let code = `$table->foreign('${column}')->references('${fk.references}')->on('${table}')`;\n\n if (fk.onDelete) {\n code += `->onDelete('${fk.onDelete}')`;\n }\n if (fk.onUpdate) {\n code += `->onUpdate('${fk.onUpdate}')`;\n }\n\n return code + ';';\n}\n\n/**\n * Formats an index to PHP code.\n */\nexport function formatIndex(index: IndexDefinition): string {\n const columns = index.columns.length === 1\n ? `'${index.columns[0]}'`\n : `[${index.columns.map(c => `'${c}'`).join(', ')}]`;\n\n const method = index.unique ? 'unique' : 'index';\n const name = index.name ? `, '${index.name}'` : '';\n\n return `$table->${method}(${columns}${name});`;\n}\n\n/**\n * Pivot table information for ManyToMany relationships.\n */\nexport interface PivotTableInfo {\n tableName: string;\n sourceTable: string;\n targetTable: string;\n sourceColumn: string;\n targetColumn: string;\n sourcePkType: 'Int' | 'BigInt' | 'Uuid' | 'String';\n targetPkType: 'Int' | 'BigInt' | 'Uuid' | 'String';\n onDelete: string | undefined;\n onUpdate: string | undefined;\n}\n\n/**\n * Polymorphic pivot table information for MorphToMany relationships.\n */\nexport interface MorphToManyPivotInfo {\n tableName: string;\n /** The fixed target schema that uses MorphToMany */\n targetTable: string;\n targetColumn: string;\n targetPkType: 'Int' | 'BigInt' | 'Uuid' | 'String';\n /** Base name for polymorphic columns (creates {name}_type and {name}_id) */\n morphName: string;\n /** Schema names that can be morphed to */\n morphTargets: readonly string[];\n onDelete: string | undefined;\n onUpdate: string | undefined;\n}\n\n/**\n * Generates pivot table name for ManyToMany relationship.\n * Uses alphabetical ordering for consistency.\n */\nexport function generatePivotTableName(\n sourceTable: string,\n targetTable: string,\n customName?: string\n): string {\n if (customName) {\n return customName;\n }\n\n // Sort alphabetically for consistent naming\n const tables = [sourceTable, targetTable].sort();\n // Remove trailing 's' and join with underscore\n const singular1 = tables[0]!.replace(/ies$/, 'y').replace(/s$/, '');\n const singular2 = tables[1]!.replace(/ies$/, 'y').replace(/s$/, '');\n return `${singular1}_${singular2}`;\n}\n\n/**\n * Extracts ManyToMany relationships from a schema.\n */\nexport function extractManyToManyRelations(\n schema: LoadedSchema,\n allSchemas: SchemaCollection\n): PivotTableInfo[] {\n const pivotTables: PivotTableInfo[] = [];\n\n if (!schema.properties) {\n return pivotTables;\n }\n\n const sourceTable = toTableName(schema.name);\n const sourcePkType = getIdType(schema);\n\n for (const [, property] of Object.entries(schema.properties)) {\n if (property.type !== 'Association') {\n continue;\n }\n\n const assocProp = property as {\n relation?: string;\n target?: string;\n joinTable?: string;\n onDelete?: string;\n onUpdate?: string;\n owning?: boolean;\n };\n\n // Only handle ManyToMany on the owning side (or if not specified, use alphabetical order)\n if (assocProp.relation !== 'ManyToMany') {\n continue;\n }\n\n const targetName = assocProp.target;\n if (!targetName) {\n continue;\n }\n\n const targetSchema = allSchemas[targetName];\n const targetTable = toTableName(targetName);\n const targetPkType = targetSchema ? getIdType(targetSchema) : 'BigInt';\n\n // Determine if this side owns the relationship\n // Default: alphabetically first schema owns it\n const isOwningSide = assocProp.owning ?? (schema.name < targetName);\n\n if (!isOwningSide) {\n continue; // Skip non-owning side to avoid duplicate pivot tables\n }\n\n const pivotTableName = generatePivotTableName(sourceTable, targetTable, assocProp.joinTable);\n\n // Column names: singular form of table name + _id\n const sourceColumn = sourceTable.replace(/ies$/, 'y').replace(/s$/, '') + '_id';\n const targetColumn = targetTable.replace(/ies$/, 'y').replace(/s$/, '') + '_id';\n\n pivotTables.push({\n tableName: pivotTableName,\n sourceTable,\n targetTable,\n sourceColumn,\n targetColumn,\n sourcePkType,\n targetPkType,\n onDelete: assocProp.onDelete,\n onUpdate: assocProp.onUpdate,\n });\n }\n\n return pivotTables;\n}\n\n/**\n * Generates blueprint for a pivot table.\n */\nexport function generatePivotTableBlueprint(pivot: PivotTableInfo): TableBlueprint {\n const columns: ColumnMethod[] = [];\n const foreignKeys: ForeignKeyDefinition[] = [];\n const indexes: IndexDefinition[] = [];\n\n // Determine column methods based on PK types\n const getMethodForPkType = (pkType: string): string => {\n switch (pkType) {\n case 'Int': return 'unsignedInteger';\n case 'Uuid': return 'uuid';\n case 'String': return 'string';\n default: return 'unsignedBigInteger';\n }\n };\n\n // Source column\n columns.push({\n name: pivot.sourceColumn,\n method: getMethodForPkType(pivot.sourcePkType),\n args: [pivot.sourceColumn],\n modifiers: [],\n });\n\n // Target column\n columns.push({\n name: pivot.targetColumn,\n method: getMethodForPkType(pivot.targetPkType),\n args: [pivot.targetColumn],\n modifiers: [],\n });\n\n // Timestamps for pivot table (Laravel's withTimestamps())\n columns.push(...generateTimestampColumns());\n\n // Foreign keys\n foreignKeys.push({\n columns: [pivot.sourceColumn],\n references: 'id',\n on: [pivot.sourceTable],\n onDelete: pivot.onDelete ?? 'cascade',\n onUpdate: pivot.onUpdate ?? 'cascade',\n });\n\n foreignKeys.push({\n columns: [pivot.targetColumn],\n references: 'id',\n on: [pivot.targetTable],\n onDelete: pivot.onDelete ?? 'cascade',\n onUpdate: pivot.onUpdate ?? 'cascade',\n });\n\n // Composite primary key / unique constraint\n indexes.push({\n columns: [pivot.sourceColumn, pivot.targetColumn],\n unique: true,\n });\n\n // Individual indexes for faster lookups (no name - let Laravel auto-generate)\n indexes.push({\n columns: [pivot.sourceColumn],\n unique: false,\n });\n\n indexes.push({\n columns: [pivot.targetColumn],\n unique: false,\n });\n\n return {\n tableName: pivot.tableName,\n columns,\n primaryKey: [pivot.sourceColumn, pivot.targetColumn],\n foreignKeys,\n indexes,\n };\n}\n\n/**\n * Extracts MorphToMany relationships from a schema.\n * MorphToMany creates a pivot table with polymorphic type/id columns.\n */\nexport function extractMorphToManyRelations(\n schema: LoadedSchema,\n allSchemas: SchemaCollection\n): MorphToManyPivotInfo[] {\n const morphPivotTables: MorphToManyPivotInfo[] = [];\n\n if (!schema.properties) {\n return morphPivotTables;\n }\n\n for (const [propName, property] of Object.entries(schema.properties)) {\n if (property.type !== 'Association') {\n continue;\n }\n\n const assocProp = property as {\n relation?: string;\n target?: string;\n joinTable?: string;\n onDelete?: string;\n onUpdate?: string;\n owning?: boolean;\n };\n\n if (assocProp.relation !== 'MorphToMany') {\n continue;\n }\n\n const targetName = assocProp.target;\n if (!targetName) {\n continue;\n }\n\n const targetSchema = allSchemas[targetName];\n const targetTable = toTableName(targetName);\n const targetPkType = targetSchema ? getIdType(targetSchema) : 'BigInt';\n\n // Determine if this side owns the relationship\n const isOwningSide = assocProp.owning ?? (schema.name < targetName);\n\n if (!isOwningSide) {\n continue;\n }\n\n // Find all schemas that have MorphedByMany pointing to this target\n // to determine the morphTargets for the ENUM\n const morphTargets: string[] = [];\n\n // The source schema itself is a morph target\n morphTargets.push(schema.name);\n\n // Look for other schemas with MorphToMany to the same target\n for (const [otherName, otherSchema] of Object.entries(allSchemas)) {\n if (otherName === schema.name) continue;\n if (!otherSchema.properties) continue;\n\n for (const otherProp of Object.values(otherSchema.properties)) {\n if (otherProp.type !== 'Association') continue;\n const otherAssoc = otherProp as { relation?: string; target?: string };\n if (otherAssoc.relation === 'MorphToMany' && otherAssoc.target === targetName) {\n if (!morphTargets.includes(otherName)) {\n morphTargets.push(otherName);\n }\n }\n }\n }\n\n // Default table name: taggables (for Tag target), commentables, etc.\n const defaultTableName = targetTable.replace(/s$/, '') + 'ables';\n const tableName = assocProp.joinTable ?? defaultTableName;\n\n // Column name for the target side (e.g., tag_id for Tag)\n const targetColumn = targetTable.replace(/ies$/, 'y').replace(/s$/, '') + '_id';\n\n // MorphName is typically the property name or a convention like 'taggable'\n const morphName = propName.replace(/s$/, '') + 'able';\n\n morphPivotTables.push({\n tableName,\n targetTable,\n targetColumn,\n targetPkType,\n morphName,\n morphTargets,\n onDelete: assocProp.onDelete,\n onUpdate: assocProp.onUpdate,\n });\n }\n\n return morphPivotTables;\n}\n\n/**\n * Generates blueprint for a polymorphic pivot table (MorphToMany).\n */\nexport function generateMorphToManyPivotBlueprint(pivot: MorphToManyPivotInfo): TableBlueprint {\n const columns: ColumnMethod[] = [];\n const foreignKeys: ForeignKeyDefinition[] = [];\n const indexes: IndexDefinition[] = [];\n\n const getMethodForPkType = (pkType: string): string => {\n switch (pkType) {\n case 'Int': return 'unsignedInteger';\n case 'Uuid': return 'uuid';\n case 'String': return 'string';\n default: return 'unsignedBigInteger';\n }\n };\n\n // Target column (e.g., tag_id)\n columns.push({\n name: pivot.targetColumn,\n method: getMethodForPkType(pivot.targetPkType),\n args: [pivot.targetColumn],\n modifiers: [],\n });\n\n // Polymorphic type column as ENUM\n const typeColumnName = `${pivot.morphName}_type`;\n columns.push({\n name: typeColumnName,\n method: 'enum',\n args: [typeColumnName, pivot.morphTargets as unknown as string],\n modifiers: [],\n });\n\n // Polymorphic ID column\n const idColumnName = `${pivot.morphName}_id`;\n columns.push({\n name: idColumnName,\n method: 'unsignedBigInteger', // Default to BigInt for polymorphic IDs\n args: [idColumnName],\n modifiers: [],\n });\n\n // Foreign key for target\n foreignKeys.push({\n columns: [pivot.targetColumn],\n references: 'id',\n on: [pivot.targetTable],\n onDelete: pivot.onDelete ?? 'cascade',\n onUpdate: pivot.onUpdate ?? 'cascade',\n });\n\n // Unique constraint for polymorphic pivot (target + morphable)\n indexes.push({\n columns: [pivot.targetColumn, typeColumnName, idColumnName],\n unique: true,\n });\n\n // Index for faster polymorphic lookups\n indexes.push({\n columns: [typeColumnName, idColumnName],\n unique: false,\n });\n\n // Index for target lookups\n indexes.push({\n columns: [pivot.targetColumn],\n unique: false,\n });\n\n return {\n tableName: pivot.tableName,\n columns,\n primaryKey: [pivot.targetColumn, typeColumnName, idColumnName],\n foreignKeys,\n indexes,\n };\n}\n\n// Note: File schema functions (hasFileProperties, schemasHaveFileProperties, generateFilesTableBlueprint)\n// have been moved to @famgia/omnify-core's schema/loader.ts\n// The File table is now managed as a proper schema (File.yaml) instead of being auto-generated in code.\n","/**\n * @famgia/omnify-laravel - Migration Generator\n *\n * Generates Laravel migration files from schemas.\n */\n\nimport type { LoadedSchema, SchemaCollection } from '@famgia/omnify-types';\nimport type {\n MigrationFile,\n MigrationOptions,\n TableBlueprint,\n} from './types.js';\nimport {\n schemaToBlueprint,\n formatColumnMethod,\n formatForeignKey,\n formatIndex,\n extractManyToManyRelations,\n generatePivotTableBlueprint,\n} from './schema-builder.js';\n\n/**\n * Generates timestamp prefix for migration file name.\n */\nfunction generateTimestamp(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n const hours = String(now.getHours()).padStart(2, '0');\n const minutes = String(now.getMinutes()).padStart(2, '0');\n const seconds = String(now.getSeconds()).padStart(2, '0');\n return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;\n}\n\n/**\n * Converts table name to Laravel migration class name.\n */\nfunction toClassName(tableName: string, type: 'create' | 'alter' | 'drop'): string {\n const pascalCase = tableName\n .split('_')\n .map(word => word.charAt(0).toUpperCase() + word.slice(1))\n .join('');\n\n switch (type) {\n case 'create':\n return `Create${pascalCase}Table`;\n case 'alter':\n return `Alter${pascalCase}Table`;\n case 'drop':\n return `Drop${pascalCase}Table`;\n }\n}\n\n/**\n * Generates file name for migration.\n */\nfunction generateFileName(\n tableName: string,\n type: 'create' | 'alter' | 'drop',\n timestamp?: string\n): string {\n const ts = timestamp ?? generateTimestamp();\n const action = type === 'create' ? 'create' : type === 'drop' ? 'drop' : 'update';\n return `${ts}_${action}_${tableName}_table.php`;\n}\n\n/**\n * Renders the up method body for a create table operation.\n */\nfunction renderCreateTableUp(blueprint: TableBlueprint): string {\n const lines: string[] = [];\n\n // Column definitions\n for (const column of blueprint.columns) {\n lines.push(` ${formatColumnMethod(column)}`);\n }\n\n // Foreign keys (separate for Laravel best practices)\n // Note: Foreign keys should be in a separate migration or at the end\n // We'll include them in the same migration for simplicity\n\n return lines.join('\\n');\n}\n\n/**\n * Renders foreign key constraints (usually added after all columns).\n */\nfunction renderForeignKeys(blueprint: TableBlueprint): string {\n if (blueprint.foreignKeys.length === 0) {\n return '';\n }\n\n const lines = blueprint.foreignKeys.map(fk => ` ${formatForeignKey(fk)}`);\n return '\\n' + lines.join('\\n');\n}\n\n/**\n * Renders indexes.\n */\nfunction renderIndexes(blueprint: TableBlueprint): string {\n // Filter out indexes that are already handled (primary key, unique columns)\n const customIndexes = blueprint.indexes.filter(idx => {\n // Skip single-column unique indexes (handled by column modifier)\n if (idx.unique && idx.columns.length === 1) {\n return false;\n }\n return true;\n });\n\n if (customIndexes.length === 0) {\n return '';\n }\n\n const lines = customIndexes.map(idx => ` ${formatIndex(idx)}`);\n return '\\n' + lines.join('\\n');\n}\n\n/**\n * Generates a create table migration.\n */\nfunction generateCreateMigration(\n blueprint: TableBlueprint,\n options: MigrationOptions = {}\n): MigrationFile {\n const className = toClassName(blueprint.tableName, 'create');\n const fileName = generateFileName(blueprint.tableName, 'create', options.timestamp);\n\n const connection = options.connection\n ? `\\n protected $connection = '${options.connection}';\\n`\n : '';\n\n const upContent = renderCreateTableUp(blueprint);\n const foreignKeyContent = renderForeignKeys(blueprint);\n const indexContent = renderIndexes(blueprint);\n\n const content = `<?php\n\nuse Illuminate\\\\Database\\\\Migrations\\\\Migration;\nuse Illuminate\\\\Database\\\\Schema\\\\Blueprint;\nuse Illuminate\\\\Support\\\\Facades\\\\Schema;\n\nreturn new class extends Migration\n{${connection}\n /**\n * Run the migrations.\n */\n public function up(): void\n {\n Schema::create('${blueprint.tableName}', function (Blueprint $table) {\n${upContent}${foreignKeyContent}${indexContent}\n });\n }\n\n /**\n * Reverse the migrations.\n */\n public function down(): void\n {\n Schema::dropIfExists('${blueprint.tableName}');\n }\n};\n`;\n\n return {\n fileName,\n className,\n content,\n tables: [blueprint.tableName],\n type: 'create',\n };\n}\n\n/**\n * Generates a drop table migration.\n */\nfunction generateDropMigration(\n tableName: string,\n options: MigrationOptions = {}\n): MigrationFile {\n const className = toClassName(tableName, 'drop');\n const fileName = generateFileName(tableName, 'drop', options.timestamp);\n\n const connection = options.connection\n ? `\\n protected $connection = '${options.connection}';\\n`\n : '';\n\n const content = `<?php\n\nuse Illuminate\\\\Database\\\\Migrations\\\\Migration;\nuse Illuminate\\\\Database\\\\Schema\\\\Blueprint;\nuse Illuminate\\\\Support\\\\Facades\\\\Schema;\n\nreturn new class extends Migration\n{${connection}\n /**\n * Run the migrations.\n */\n public function up(): void\n {\n Schema::dropIfExists('${tableName}');\n }\n\n /**\n * Reverse the migrations.\n */\n public function down(): void\n {\n // Cannot recreate table without schema information\n // This is a one-way migration\n }\n};\n`;\n\n return {\n fileName,\n className,\n content,\n tables: [tableName],\n type: 'drop',\n };\n}\n\n/**\n * Extracts FK dependencies from a schema.\n * Returns array of schema names that this schema depends on.\n */\nfunction extractDependencies(schema: LoadedSchema): string[] {\n const deps: string[] = [];\n\n if (!schema.properties) {\n return deps;\n }\n\n for (const property of Object.values(schema.properties)) {\n if (property.type !== 'Association') {\n continue;\n }\n\n const assocProp = property as {\n relation?: string;\n target?: string;\n mappedBy?: string;\n };\n\n // ManyToOne and OneToOne (owning side) create FK columns\n if (\n (assocProp.relation === 'ManyToOne' || assocProp.relation === 'OneToOne') &&\n !assocProp.mappedBy &&\n assocProp.target\n ) {\n deps.push(assocProp.target);\n }\n }\n\n return deps;\n}\n\n/**\n * Topological sort of schemas based on FK dependencies.\n * Returns schemas in order where dependencies come before dependents.\n */\nfunction topologicalSort(schemas: SchemaCollection): LoadedSchema[] {\n const schemaList = Object.values(schemas).filter(s => s.kind !== 'enum');\n const sorted: LoadedSchema[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>(); // For cycle detection\n\n function visit(schema: LoadedSchema): void {\n if (visited.has(schema.name)) {\n return;\n }\n\n if (visiting.has(schema.name)) {\n // Circular dependency - just continue (FK can be nullable)\n return;\n }\n\n visiting.add(schema.name);\n\n // Visit dependencies first\n const deps = extractDependencies(schema);\n for (const depName of deps) {\n const depSchema = schemas[depName];\n if (depSchema && depSchema.kind !== 'enum') {\n visit(depSchema);\n }\n }\n\n visiting.delete(schema.name);\n visited.add(schema.name);\n sorted.push(schema);\n }\n\n // Visit all schemas\n for (const schema of schemaList) {\n visit(schema);\n }\n\n return sorted;\n}\n\n/**\n * Generates migrations for all schemas.\n */\nexport function generateMigrations(\n schemas: SchemaCollection,\n options: MigrationOptions = {}\n): MigrationFile[] {\n const migrations: MigrationFile[] = [];\n const pivotTablesGenerated = new Set<string>();\n let timestampOffset = 0;\n\n // Note: File table (for polymorphic file storage) is now managed as a proper schema (File.yaml)\n // It will be processed like any other schema in the topological sort below.\n // Use ensureFileSchema() from @famgia/omnify-core to auto-generate File.yaml when needed.\n\n // Sort schemas by FK dependencies (topological sort)\n const sortedSchemas = topologicalSort(schemas);\n\n // Generate main table migrations in dependency order\n for (const schema of sortedSchemas) {\n // Generate timestamp with offset to ensure unique filenames\n const timestamp = options.timestamp ?? generateTimestamp();\n const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);\n timestampOffset++;\n\n const blueprint = schemaToBlueprint(schema, schemas, {\n customTypes: options.customTypes,\n locale: options.locale,\n });\n const migration = generateCreateMigration(blueprint, {\n ...options,\n timestamp: offsetTimestamp,\n });\n migrations.push(migration);\n }\n\n // Third pass: generate pivot table migrations for ManyToMany (always last)\n for (const schema of sortedSchemas) {\n const pivotTables = extractManyToManyRelations(schema, schemas);\n\n for (const pivot of pivotTables) {\n // Skip if already generated\n if (pivotTablesGenerated.has(pivot.tableName)) {\n continue;\n }\n pivotTablesGenerated.add(pivot.tableName);\n\n const timestamp = options.timestamp ?? generateTimestamp();\n const offsetTimestamp = incrementTimestamp(timestamp, timestampOffset);\n timestampOffset++;\n\n const blueprint = generatePivotTableBlueprint(pivot);\n const migration = generateCreateMigration(blueprint, {\n ...options,\n timestamp: offsetTimestamp,\n });\n migrations.push(migration);\n }\n }\n\n return migrations;\n}\n\n/**\n * Increments a timestamp by seconds.\n */\nfunction incrementTimestamp(timestamp: string, seconds: number): string {\n if (seconds === 0) return timestamp;\n\n // Parse timestamp: YYYY_MM_DD_HHMMSS\n const parts = timestamp.split('_');\n if (parts.length < 4) {\n // Invalid format, return original\n return timestamp;\n }\n\n const yearPart = parts[0] ?? '2024';\n const monthPart = parts[1] ?? '01';\n const dayPart = parts[2] ?? '01';\n const timePart = parts[3] ?? '000000';\n\n const year = parseInt(yearPart, 10);\n const month = parseInt(monthPart, 10) - 1;\n const day = parseInt(dayPart, 10);\n const hours = parseInt(timePart.substring(0, 2), 10);\n const minutes = parseInt(timePart.substring(2, 4), 10);\n const secs = parseInt(timePart.substring(4, 6), 10);\n\n const date = new Date(year, month, day, hours, minutes, secs + seconds);\n\n const newYear = date.getFullYear();\n const newMonth = String(date.getMonth() + 1).padStart(2, '0');\n const newDay = String(date.getDate()).padStart(2, '0');\n const newHours = String(date.getHours()).padStart(2, '0');\n const newMinutes = String(date.getMinutes()).padStart(2, '0');\n const newSecs = String(date.getSeconds()).padStart(2, '0');\n\n return `${newYear}_${newMonth}_${newDay}_${newHours}${newMinutes}${newSecs}`;\n}\n\n/**\n * Generates migration from a single schema.\n */\nexport function generateMigrationFromSchema(\n schema: LoadedSchema,\n allSchemas: SchemaCollection,\n options: MigrationOptions = {}\n): MigrationFile {\n const blueprint = schemaToBlueprint(schema, allSchemas);\n return generateCreateMigration(blueprint, options);\n}\n\n/**\n * Generates drop migration for a table.\n */\nexport function generateDropMigrationForTable(\n tableName: string,\n options: MigrationOptions = {}\n): MigrationFile {\n return generateDropMigration(tableName, options);\n}\n\n/**\n * Formats migration content for writing to file.\n */\nexport function formatMigrationFile(migration: MigrationFile): string {\n return migration.content;\n}\n\n/**\n * Gets the output path for a migration file.\n */\nexport function getMigrationPath(\n migration: MigrationFile,\n outputDir: string = 'database/migrations'\n): string {\n return `${outputDir}/${migration.fileName}`;\n}\n","/**\n * @famgia/omnify-laravel - ALTER Migration Generator\n *\n * Generates Laravel migration files for ALTER table operations.\n */\n\nimport type { SchemaChange, PropertySnapshot } from '@famgia/omnify-atlas';\nimport type { MigrationFile, MigrationOptions } from './types.js';\nimport { toTableName, toColumnName } from './schema-builder.js';\n\n/**\n * Maps Omnify property types to Laravel column methods.\n */\nconst TYPE_METHOD_MAP: Record<string, string> = {\n String: 'string',\n TinyInt: 'tinyInteger',\n Int: 'integer',\n BigInt: 'bigInteger',\n Float: 'double',\n Decimal: 'decimal',\n Boolean: 'boolean',\n Text: 'text',\n MediumText: 'mediumText',\n LongText: 'longText',\n Date: 'date',\n Time: 'time',\n DateTime: 'dateTime',\n Timestamp: 'timestamp',\n Json: 'json',\n Email: 'string',\n Password: 'string',\n File: 'string',\n MultiFile: 'json',\n Enum: 'enum',\n Select: 'string',\n Lookup: 'unsignedBigInteger',\n};\n\n/**\n * Generates timestamp prefix for migration file name.\n */\nfunction generateTimestamp(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n const hours = String(now.getHours()).padStart(2, '0');\n const minutes = String(now.getMinutes()).padStart(2, '0');\n const seconds = String(now.getSeconds()).padStart(2, '0');\n return `${year}_${month}_${day}_${hours}${minutes}${seconds}`;\n}\n\n/**\n * Formats a column addition.\n */\nfunction formatAddColumn(columnName: string, prop: PropertySnapshot): string {\n const snakeColumn = toColumnName(columnName);\n const method = TYPE_METHOD_MAP[prop.type] ?? 'string';\n\n let code: string;\n\n // Handle decimal with precision and scale\n if (prop.type === 'Decimal') {\n const precision = prop.precision ?? 8;\n const scale = prop.scale ?? 2;\n code = `$table->${method}('${snakeColumn}', ${precision}, ${scale})`;\n } else {\n code = `$table->${method}('${snakeColumn}')`;\n }\n\n // Add modifiers\n if (prop.nullable) code += '->nullable()';\n if (prop.unique) code += '->unique()';\n if (prop.default !== undefined) {\n const defaultValue = typeof prop.default === 'string'\n ? `'${prop.default}'`\n : JSON.stringify(prop.default);\n code += `->default(${defaultValue})`;\n }\n\n return code + ';';\n}\n\n/**\n * Formats a column removal.\n */\nfunction formatDropColumn(columnName: string): string {\n const snakeColumn = toColumnName(columnName);\n return `$table->dropColumn('${snakeColumn}');`;\n}\n\n/**\n * Formats a column rename.\n * Note: Requires doctrine/dbal package in Laravel.\n */\nfunction formatRenameColumn(oldName: string, newName: string): string {\n const oldSnake = toColumnName(oldName);\n const newSnake = toColumnName(newName);\n return `$table->renameColumn('${oldSnake}', '${newSnake}');`;\n}\n\n/**\n * Formats a column modification.\n * Note: Requires doctrine/dbal package in Laravel.\n */\nfunction formatModifyColumn(\n columnName: string,\n _prevProp: PropertySnapshot,\n currProp: PropertySnapshot\n): string {\n const snakeColumn = toColumnName(columnName);\n const method = TYPE_METHOD_MAP[currProp.type] ?? 'string';\n\n let code: string;\n\n // Handle decimal with precision and scale\n if (currProp.type === 'Decimal') {\n const precision = currProp.precision ?? 8;\n const scale = currProp.scale ?? 2;\n code = `$table->${method}('${snakeColumn}', ${precision}, ${scale})`;\n } else {\n code = `$table->${method}('${snakeColumn}')`;\n }\n\n // Add modifiers\n if (currProp.nullable) code += '->nullable()';\n if (currProp.unique) code += '->unique()';\n if (currProp.default !== undefined) {\n const defaultValue = typeof currProp.default === 'string'\n ? `'${currProp.default}'`\n : JSON.stringify(currProp.default);\n code += `->default(${defaultValue})`;\n }\n\n return code + '->change();';\n}\n\n/**\n * Formats an index addition.\n */\nfunction formatAddIndex(columns: readonly string[], unique: boolean): string {\n const snakeColumns = columns.map(toColumnName);\n const method = unique ? 'unique' : 'index';\n const colsArg = snakeColumns.length === 1\n ? `'${snakeColumns[0]}'`\n : `[${snakeColumns.map(c => `'${c}'`).join(', ')}]`;\n\n return `$table->${method}(${colsArg});`;\n}\n\n/**\n * Formats an index removal.\n */\nfunction formatDropIndex(tableName: string, columns: readonly string[], unique: boolean): string {\n const snakeColumns = columns.map(toColumnName);\n const method = unique ? 'dropUnique' : 'dropIndex';\n\n // Laravel generates index names as: {table}_{columns}_{type}\n const suffix = unique ? 'unique' : 'index';\n const indexName = `${tableName}_${snakeColumns.join('_')}_${suffix}`;\n\n return `$table->${method}('${indexName}');`;\n}\n\n/**\n * Generates ALTER migration content for a schema change.\n */\nfunction generateAlterMigrationContent(\n tableName: string,\n change: SchemaChange,\n options: MigrationOptions = {}\n): string {\n const upLines: string[] = [];\n const downLines: string[] = [];\n\n // Column changes\n if (change.columnChanges) {\n for (const col of change.columnChanges) {\n if (col.changeType === 'added' && col.currentDef) {\n upLines.push(` ${formatAddColumn(col.column, col.currentDef)}`);\n downLines.push(` ${formatDropColumn(col.column)}`);\n } else if (col.changeType === 'removed' && col.previousDef) {\n upLines.push(` ${formatDropColumn(col.column)}`);\n downLines.push(` ${formatAddColumn(col.column, col.previousDef)}`);\n } else if (col.changeType === 'modified' && col.previousDef && col.currentDef) {\n upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);\n downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);\n } else if (col.changeType === 'renamed' && col.previousColumn) {\n // Rename column\n upLines.push(` ${formatRenameColumn(col.previousColumn, col.column)}`);\n downLines.push(` ${formatRenameColumn(col.column, col.previousColumn)}`);\n\n // If there are also property modifications, apply them after rename\n if (col.modifications && col.modifications.length > 0 && col.previousDef && col.currentDef) {\n upLines.push(` ${formatModifyColumn(col.column, col.previousDef, col.currentDef)}`);\n downLines.push(` ${formatModifyColumn(col.column, col.currentDef, col.previousDef)}`);\n }\n }\n }\n }\n\n // Index changes\n if (change.indexChanges) {\n for (const idx of change.indexChanges) {\n if (idx.changeType === 'added') {\n upLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);\n downLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);\n } else {\n upLines.push(` ${formatDropIndex(tableName, idx.index.columns, idx.index.unique)}`);\n downLines.push(` ${formatAddIndex(idx.index.columns, idx.index.unique)}`);\n }\n }\n }\n\n // Option changes (timestamps, softDelete)\n if (change.optionChanges) {\n if (change.optionChanges.timestamps) {\n const { from, to } = change.optionChanges.timestamps;\n if (to && !from) {\n upLines.push(` $table->timestamps();`);\n downLines.push(` $table->dropTimestamps();`);\n } else if (from && !to) {\n upLines.push(` $table->dropTimestamps();`);\n downLines.push(` $table->timestamps();`);\n }\n }\n\n if (change.optionChanges.softDelete) {\n const { from, to } = change.optionChanges.softDelete;\n if (to && !from) {\n upLines.push(` $table->softDeletes();`);\n downLines.push(` $table->dropSoftDeletes();`);\n } else if (from && !to) {\n upLines.push(` $table->dropSoftDeletes();`);\n downLines.push(` $table->softDeletes();`);\n }\n }\n }\n\n const connection = options.connection\n ? `\\n protected $connection = '${options.connection}';\\n`\n : '';\n\n return `<?php\n\nuse Illuminate\\\\Database\\\\Migrations\\\\Migration;\nuse Illuminate\\\\Database\\\\Schema\\\\Blueprint;\nuse Illuminate\\\\Support\\\\Facades\\\\Schema;\n\nreturn new class extends Migration\n{${connection}\n /**\n * Run the migrations.\n */\n public function up(): void\n {\n Schema::table('${tableName}', function (Blueprint $table) {\n${upLines.join('\\n')}\n });\n }\n\n /**\n * Reverse the migrations.\n */\n public function down(): void\n {\n Schema::table('${tableName}', function (Blueprint $table) {\n${downLines.join('\\n')}\n });\n }\n};\n`;\n}\n\n/**\n * Generates ALTER migration for a modified schema.\n */\nexport function generateAlterMigration(\n change: SchemaChange,\n options: MigrationOptions = {}\n): MigrationFile | null {\n if (change.changeType !== 'modified') {\n return null;\n }\n\n // Check if there are actual changes to migrate\n const hasChanges =\n (change.columnChanges && change.columnChanges.length > 0) ||\n (change.indexChanges && change.indexChanges.length > 0) ||\n (change.optionChanges &&\n (change.optionChanges.timestamps ||\n change.optionChanges.softDelete));\n\n if (!hasChanges) {\n return null;\n }\n\n const tableName = toTableName(change.schemaName);\n const timestamp = options.timestamp ?? generateTimestamp();\n const fileName = `${timestamp}_update_${tableName}_table.php`;\n\n const content = generateAlterMigrationContent(tableName, change, options);\n\n return {\n fileName,\n className: `Update${change.schemaName}Table`,\n content,\n tables: [tableName],\n type: 'alter',\n };\n}\n\n/**\n * Generates DROP migration for a removed schema.\n */\nexport function generateDropTableMigration(\n schemaName: string,\n options: MigrationOptions = {}\n): MigrationFile {\n const tableName = toTableName(schemaName);\n const timestamp = options.timestamp ?? generateTimestamp();\n const fileName = `${timestamp}_drop_${tableName}_table.php`;\n\n const connection = options.connection\n ? `\\n protected $connection = '${options.connection}';\\n`\n : '';\n\n const content = `<?php\n\nuse Illuminate\\\\Database\\\\Migrations\\\\Migration;\nuse Illuminate\\\\Database\\\\Schema\\\\Blueprint;\nuse Illuminate\\\\Support\\\\Facades\\\\Schema;\n\nreturn new class extends Migration\n{${connection}\n /**\n * Run the migrations.\n */\n public function up(): void\n {\n Schema::dropIfExists('${tableName}');\n }\n\n /**\n * Reverse the migrations.\n */\n public function down(): void\n {\n // Cannot recreate table without full schema\n // Consider restoring from backup if needed\n }\n};\n`;\n\n return {\n fileName,\n className: `Drop${schemaName}Table`,\n content,\n tables: [tableName],\n type: 'drop',\n };\n}\n\n/**\n * Generates migrations for all schema changes.\n */\nexport function generateMigrationsFromChanges(\n changes: readonly SchemaChange[],\n options: MigrationOptions = {}\n): MigrationFile[] {\n const migrations: MigrationFile[] = [];\n let timestampOffset = 0;\n\n const getNextTimestamp = () => {\n const ts = options.timestamp ?? generateTimestamp();\n const offset = timestampOffset++;\n if (offset === 0) return ts;\n\n // Increment seconds\n const parts = ts.split('_');\n if (parts.length >= 4) {\n const timePart = parts[3] ?? '000000';\n const secs = parseInt(timePart.substring(4, 6), 10) + offset;\n const newSecs = String(secs % 60).padStart(2, '0');\n parts[3] = timePart.substring(0, 4) + newSecs;\n return parts.join('_');\n }\n return ts;\n };\n\n for (const change of changes) {\n if (change.changeType === 'modified') {\n const migration = generateAlterMigration(change, {\n ...options,\n timestamp: getNextTimestamp(),\n });\n if (migration) {\n migrations.push(migration);\n }\n } else if (change.changeType === 'removed') {\n migrations.push(\n generateDropTableMigration(change.schemaName, {\n ...options,\n timestamp: getNextTimestamp(),\n })\n );\n }\n // 'added' changes are handled by the regular CREATE migration generator\n }\n\n return migrations;\n}\n","/**\n * Laravel Model Generator\n *\n * Generates Eloquent model classes from Omnify schemas.\n * Creates base models (auto-generated) and user models (created once).\n */\n\nimport type { LoadedSchema, PropertyDefinition, AssociationDefinition, SchemaCollection, LocalizedString, CustomTypeDefinition } from '@famgia/omnify-types';\nimport { isLocaleMap } from '@famgia/omnify-types';\nimport { pluralize, toSnakeCase, toPascalCase, toCamelCase } from '../utils.js';\n\n/**\n * Options for model generation.\n */\nexport interface ModelGeneratorOptions {\n /**\n * Base model namespace.\n * @default 'App\\\\Models\\\\OmnifyBase'\n */\n baseModelNamespace?: string;\n\n /**\n * User model namespace.\n * @default 'App\\\\Models'\n */\n modelNamespace?: string;\n\n /**\n * Base model class name.\n * @default 'BaseModel'\n */\n baseModelClassName?: string;\n\n /**\n * Output path for base models.\n * @default 'app/Models/OmnifyBase'\n */\n baseModelPath?: string;\n\n /**\n * Output path for user models.\n * @default 'app/Models'\n */\n modelPath?: string;\n\n /**\n * Output path for service provider files.\n * @default 'app/Providers'\n */\n providersPath?: string;\n\n /**\n * Custom types registered by plugins.\n * Used to expand compound types in fillable array.\n */\n customTypes?: ReadonlyMap<string, CustomTypeDefinition>;\n}\n\n/**\n * Generated model output.\n */\nexport interface GeneratedModel {\n /** File path relative to project root */\n path: string;\n /** PHP content */\n content: string;\n /** Model type */\n type: 'base-model' | 'entity-base' | 'entity' | 'service-provider' | 'provider-registration' | 'trait' | 'locales';\n /** Whether to overwrite existing file */\n overwrite: boolean;\n /** Schema name */\n schemaName: string;\n}\n\n/**\n * Provider registration result.\n */\nexport interface ProviderRegistrationResult {\n /** Path to the provider registration file */\n path: string;\n /** Modified content */\n content: string;\n /** Laravel version type */\n laravelVersion: 'laravel11+' | 'laravel10-';\n /** Whether registration was already present */\n alreadyRegistered: boolean;\n}\n\n/**\n * Resolved options with defaults.\n */\ninterface ResolvedOptions {\n baseModelNamespace: string;\n modelNamespace: string;\n baseModelClassName: string;\n baseModelPath: string;\n modelPath: string;\n providersPath: string;\n customTypes: ReadonlyMap<string, CustomTypeDefinition>;\n}\n\n/**\n * Default options.\n */\nconst DEFAULT_OPTIONS: ResolvedOptions = {\n baseModelNamespace: 'App\\\\Models\\\\OmnifyBase',\n modelNamespace: 'App\\\\Models',\n baseModelClassName: 'BaseModel',\n baseModelPath: 'app/Models/OmnifyBase',\n modelPath: 'app/Models',\n providersPath: 'app/Providers',\n customTypes: new Map(),\n};\n\n/**\n * Generate PHP array entries for localized display names.\n * Converts LocalizedString to PHP array format.\n */\nfunction generateLocalizedDisplayNames(displayName: LocalizedString | undefined, indent: string = ' '): string {\n if (displayName === undefined) {\n return '';\n }\n\n if (typeof displayName === 'string') {\n // Single string - use 'en' as default locale\n return `${indent}'en' => '${escapePhpString(displayName)}',`;\n }\n\n if (isLocaleMap(displayName)) {\n const entries = Object.entries(displayName)\n .map(([locale, value]) => `${indent}'${locale}' => '${escapePhpString(value)}',`)\n .join('\\n');\n return entries;\n }\n\n return '';\n}\n\n/**\n * Generate PHP array entries for property localized display names.\n */\nfunction generatePropertyLocalizedDisplayNames(\n schema: LoadedSchema,\n indent: string = ' '\n): string {\n const properties = schema.properties ?? {};\n const entries: string[] = [];\n\n for (const [propName, propDef] of Object.entries(properties)) {\n const snakeName = toSnakeCase(propName);\n const displayName = (propDef as { displayName?: LocalizedString }).displayName;\n\n if (displayName === undefined) {\n continue;\n }\n\n const innerIndent = indent + ' ';\n\n if (typeof displayName === 'string') {\n entries.push(`${indent}'${snakeName}' => [\\n${innerIndent}'en' => '${escapePhpString(displayName)}',\\n${indent}],`);\n } else if (isLocaleMap(displayName)) {\n const localeEntries = Object.entries(displayName)\n .map(([locale, value]) => `${innerIndent}'${locale}' => '${escapePhpString(value)}',`)\n .join('\\n');\n entries.push(`${indent}'${snakeName}' => [\\n${localeEntries}\\n${indent}],`);\n }\n }\n\n return entries.join('\\n');\n}\n\n/**\n * Escape string for PHP single-quoted string.\n */\nfunction escapePhpString(str: string): string {\n return str.replace(/\\\\/g, '\\\\\\\\').replace(/'/g, \"\\\\'\");\n}\n\n/**\n * Resolve options with defaults.\n */\nfunction resolveOptions(options?: ModelGeneratorOptions): ResolvedOptions {\n return {\n baseModelNamespace: options?.baseModelNamespace ?? DEFAULT_OPTIONS.baseModelNamespace,\n modelNamespace: options?.modelNamespace ?? DEFAULT_OPTIONS.modelNamespace,\n baseModelClassName: options?.baseModelClassName ?? DEFAULT_OPTIONS.baseModelClassName,\n baseModelPath: options?.baseModelPath ?? DEFAULT_OPTIONS.baseModelPath,\n modelPath: options?.modelPath ?? DEFAULT_OPTIONS.modelPath,\n providersPath: options?.providersPath ?? DEFAULT_OPTIONS.providersPath,\n customTypes: options?.customTypes ?? new Map(),\n };\n}\n\n/**\n * Get PHP type for casting.\n */\nfunction getCastType(propDef: PropertyDefinition): string | null {\n switch (propDef.type) {\n case 'Boolean':\n return 'boolean';\n case 'Int':\n case 'BigInt':\n return 'integer';\n case 'Float':\n return 'float';\n case 'Decimal':\n return 'decimal:' + ((propDef as any).scale ?? 2);\n case 'Json':\n return 'array';\n case 'Date':\n return 'date';\n case 'DateTime':\n case 'Timestamp':\n return 'datetime';\n case 'Password':\n return 'hashed';\n default:\n return null;\n }\n}\n\n/**\n * Check if a property definition is nullable.\n * For associations, check the 'nullable' field if it exists as an extension.\n */\nfunction isNullable(propDef: PropertyDefinition): boolean {\n // BasePropertyDefinition has nullable, AssociationDefinition does not\n // But some schemas may define nullable on associations as an extension\n return 'nullable' in propDef && propDef.nullable === true;\n}\n\n/**\n * Get PHP doc type for a property.\n */\nfunction getPhpDocType(propDef: PropertyDefinition, schemas: SchemaCollection): string {\n const nullable = isNullable(propDef);\n\n switch (propDef.type) {\n case 'String':\n case 'Text':\n case 'LongText':\n case 'Email':\n case 'Password':\n return 'string' + (nullable ? '|null' : '');\n case 'Int':\n case 'BigInt':\n return 'int' + (nullable ? '|null' : '');\n case 'Float':\n case 'Decimal':\n return 'float' + (nullable ? '|null' : '');\n case 'Boolean':\n return 'bool' + (nullable ? '|null' : '');\n case 'Date':\n case 'DateTime':\n case 'Time':\n case 'Timestamp':\n return '\\\\Carbon\\\\Carbon' + (nullable ? '|null' : '');\n case 'Json':\n return 'array' + (nullable ? '|null' : '');\n case 'Enum':\n case 'EnumRef':\n return 'string' + (nullable ? '|null' : '');\n case 'Association': {\n const assoc = propDef as AssociationDefinition;\n if (assoc.target) {\n const className = toPascalCase(assoc.target);\n switch (assoc.relation) {\n case 'OneToMany':\n case 'ManyToMany':\n case 'MorphMany':\n case 'MorphToMany':\n case 'MorphedByMany':\n return `\\\\Illuminate\\\\Database\\\\Eloquent\\\\Collection<${className}>`;\n default:\n // ManyToOne, OneToOne - typically nullable unless owning\n return className + '|null';\n }\n }\n return 'mixed';\n }\n default:\n return 'mixed';\n }\n}\n\n/**\n * Expand compound type property into field names.\n * Returns array of field names (snake_case) or null if not a compound type.\n */\nfunction expandCompoundTypeFields(\n propName: string,\n propType: string,\n customTypes: ReadonlyMap<string, CustomTypeDefinition>\n): string[] | null {\n const typeDef = customTypes.get(propType);\n\n if (!typeDef || !typeDef.compound || !typeDef.expand) {\n return null;\n }\n\n const snakeName = toSnakeCase(propName);\n const fields: string[] = [];\n\n for (const field of typeDef.expand) {\n // Convert suffix to snake_case and combine with property name\n const suffixSnake = toSnakeCase(field.suffix);\n fields.push(`${snakeName}_${suffixSnake}`);\n }\n\n return fields;\n}\n\n/**\n * Generate the shared BaseModel class.\n */\nfunction generateBaseModel(\n schemas: SchemaCollection,\n options: ResolvedOptions,\n stubContent: string\n): GeneratedModel {\n // Build model map\n const modelMap = Object.values(schemas)\n .filter(s => s.kind !== 'enum')\n .map(s => {\n const className = toPascalCase(s.name);\n return ` '${s.name}' => \\\\${options.modelNamespace}\\\\${className}::class,`;\n })\n .join('\\n');\n\n const content = stubContent\n .replace(/\\{\\{BASE_MODEL_NAMESPACE\\}\\}/g, options.baseModelNamespace)\n .replace(/\\{\\{BASE_MODEL_CLASS\\}\\}/g, options.baseModelClassName)\n .replace(/\\{\\{MODEL_MAP\\}\\}/g, modelMap);\n\n return {\n path: `${options.baseModelPath}/${options.baseModelClassName}.php`,\n content,\n type: 'base-model',\n overwrite: true,\n schemaName: '__base__',\n };\n}\n\n/**\n * Generate entity base model (auto-generated).\n */\nfunction generateEntityBaseModel(\n schema: LoadedSchema,\n schemas: SchemaCollection,\n options: ResolvedOptions,\n stubContent: string,\n authStubContent: string\n): GeneratedModel {\n const className = toPascalCase(schema.name);\n const tableName = schema.options?.tableName ?? pluralize(toSnakeCase(schema.name));\n const isAuth = schema.options?.authenticatable ?? false;\n\n // Determine primary key\n // Check if id is disabled and we need to find a custom primary key\n const hasAutoId = schema.options?.id !== false;\n let primaryKey = 'id';\n let isStringKey = false;\n let isUuid = false;\n let isNonIncrementing = false;\n\n if (hasAutoId) {\n // Standard auto-incrementing or UUID id\n const idType = schema.options?.idType ?? 'BigInt';\n isUuid = idType === 'Uuid';\n isStringKey = idType === 'Uuid' || idType === 'String';\n isNonIncrementing = isUuid;\n } else {\n // Custom primary key - find the property with primary: true\n const properties = schema.properties ?? {};\n for (const [propName, propDef] of Object.entries(properties)) {\n if ((propDef as { primary?: boolean }).primary === true) {\n primaryKey = toSnakeCase(propName);\n // Determine if it's a string-type key\n const propType = propDef.type;\n isStringKey = propType === 'String' || propType === 'Text' || propType === 'Email';\n isNonIncrementing = true; // Custom primary keys are not auto-incrementing\n break;\n }\n }\n }\n\n // Build imports, traits, fillable, hidden, casts, relations\n const imports: string[] = [];\n const traits: string[] = [];\n const fillable: string[] = [];\n const hidden: string[] = [];\n const appends: string[] = [];\n const casts: string[] = [];\n const relations: string[] = [];\n const docProperties: string[] = [];\n\n // Add soft delete\n if (schema.options?.softDelete) {\n imports.push('use Illuminate\\\\Database\\\\Eloquent\\\\SoftDeletes;');\n traits.push(' use SoftDeletes;');\n }\n\n // Process properties\n const properties = schema.properties ?? {};\n for (const [propName, propDef] of Object.entries(properties)) {\n const snakeName = toSnakeCase(propName);\n\n // Check if this is a compound type (defer docProperties to expanded fields)\n const typeDef = options.customTypes.get(propDef.type);\n const isCompoundType = typeDef?.compound && typeDef.expand;\n\n // Add to doc comments (skip for compound types - they'll be added per field)\n if (!isCompoundType) {\n const phpType = getPhpDocType(propDef, schemas);\n docProperties.push(` * @property ${phpType} $${snakeName}`);\n }\n\n if (propDef.type === 'Association') {\n const assoc = propDef as AssociationDefinition;\n if (assoc.target) {\n imports.push(`use ${options.modelNamespace}\\\\${toPascalCase(assoc.target)};`);\n }\n relations.push(generateRelation(propName, assoc, schema, schemas, options));\n\n // Add foreign key to fillable for belongsTo relations\n if (assoc.relation === 'ManyToOne' || assoc.relation === 'OneToOne') {\n if (!assoc.mappedBy) {\n const fkName = toSnakeCase(propName) + '_id';\n fillable.push(` '${fkName}',`);\n docProperties.push(` * @property int|null $${fkName}`);\n }\n }\n } else if (propDef.type === 'Password') {\n // Check if fillable: false is set\n const propWithFillable = propDef as { fillable?: boolean };\n if (propWithFillable.fillable !== false) {\n fillable.push(` '${snakeName}',`);\n }\n hidden.push(` '${snakeName}',`);\n const cast = getCastType(propDef);\n if (cast) {\n casts.push(` '${snakeName}' => '${cast}',`);\n }\n } else if (propDef.type === 'File') {\n // File properties don't add to fillable (polymorphic)\n const relMethod = generateFileRelation(propName, propDef as any);\n relations.push(relMethod);\n } else {\n // Check for fillable property (default: true)\n const propWithOptions = propDef as { fillable?: boolean; hidden?: boolean; fields?: Record<string, { nullable?: boolean; hidden?: boolean; fillable?: boolean }> };\n const isFillable = propWithOptions.fillable !== false;\n const isHidden = propWithOptions.hidden === true;\n\n // Check if this is a compound type that should be expanded\n const typeDef = options.customTypes.get(propDef.type);\n const isCompoundType = typeDef?.compound && typeDef.expand;\n\n if (isCompoundType && typeDef.expand) {\n // Compound type - process each expanded field with per-field overrides\n const fieldOverrides = propWithOptions.fields ?? {};\n const basePropWithNullable = propDef as { nullable?: boolean };\n\n for (const field of typeDef.expand) {\n const suffixSnake = toSnakeCase(field.suffix);\n const fieldName = `${snakeName}_${suffixSnake}`;\n const override = fieldOverrides[field.suffix];\n\n // Determine nullable for PHP type\n const fieldNullable = override?.nullable ?? basePropWithNullable.nullable ?? false;\n const phpType = field.typescript?.type === 'number' ? 'int' : 'string';\n const nullSuffix = fieldNullable ? '|null' : '';\n docProperties.push(` * @property ${phpType}${nullSuffix} $${fieldName}`);\n\n // Determine fillable: use field override if set, otherwise use property-level setting\n const fieldFillable = override?.fillable !== undefined ? override.fillable : isFillable;\n if (fieldFillable) {\n fillable.push(` '${fieldName}',`);\n }\n\n // Determine hidden: use field override if set, otherwise use property-level setting\n const fieldHidden = override?.hidden !== undefined ? override.hidden : isHidden;\n if (fieldHidden) {\n hidden.push(` '${fieldName}',`);\n }\n }\n } else {\n // Regular property\n if (isFillable) {\n fillable.push(` '${snakeName}',`);\n }\n\n const cast = getCastType(propDef);\n if (cast) {\n casts.push(` '${snakeName}' => '${cast}',`);\n }\n\n // Check for hidden: true property (for non-Password types)\n if (isHidden) {\n hidden.push(` '${snakeName}',`);\n }\n }\n\n // Check for compound type accessors (reuse typeDef from above)\n if (typeDef?.compound && typeDef.accessors) {\n for (const accessor of typeDef.accessors) {\n // Generate accessor name with property prefix\n const accessorName = `${snakeName}_${toSnakeCase(accessor.name)}`;\n appends.push(` '${accessorName}',`);\n\n // Generate accessor method\n const methodName = toPascalCase(accessorName);\n const separator = accessor.separator ?? ' ';\n\n // Build field references\n const fieldRefs = accessor.fields.map(field => {\n const fieldName = `${snakeName}_${toSnakeCase(field)}`;\n return `$this->${fieldName}`;\n });\n\n // Generate accessor method with null filtering for optional fields\n const accessorMethod = ` /**\n * Get the ${accessor.name.replace(/_/g, ' ')} attribute.\n */\n public function get${methodName}Attribute(): ?string\n {\n $parts = array_filter([${fieldRefs.join(', ')}], fn($v) => $v !== null && $v !== '');\n return count($parts) > 0 ? implode('${separator}', $parts) : null;\n }`;\n relations.push(accessorMethod);\n }\n }\n }\n }\n\n // Build doc comment\n const docComment = `/**\n * ${className}BaseModel\n *\n${docProperties.join('\\n')}\n */`;\n\n // Choose stub based on authenticatable\n const stub = isAuth ? authStubContent : stubContent;\n\n // Build key type and incrementing\n const keyType = isStringKey ? ` /**\n * The \"type\" of the primary key ID.\n */\n protected $keyType = 'string';\n\n` : '';\n\n const incrementing = isNonIncrementing ? ` /**\n * Indicates if the IDs are auto-incrementing.\n */\n public $incrementing = false;\n\n` : '';\n\n // Add UUID trait if needed\n if (isUuid) {\n imports.push('use Illuminate\\\\Database\\\\Eloquent\\\\Concerns\\\\HasUuids;');\n traits.push(' use HasUuids;');\n }\n\n const content = stub\n .replace(/\\{\\{BASE_MODEL_NAMESPACE\\}\\}/g, options.baseModelNamespace)\n .replace(/\\{\\{BASE_MODEL_CLASS\\}\\}/g, options.baseModelClassName)\n .replace(/\\{\\{CLASS_NAME\\}\\}/g, className)\n .replace(/\\{\\{TABLE_NAME\\}\\}/g, tableName)\n .replace(/\\{\\{PRIMARY_KEY\\}\\}/g, primaryKey)\n .replace(/\\{\\{KEY_TYPE\\}\\}/g, keyType)\n .replace(/\\{\\{INCREMENTING\\}\\}/g, incrementing)\n .replace(/\\{\\{TIMESTAMPS\\}\\}/g, schema.options?.timestamps !== false ? 'true' : 'false')\n .replace(/\\{\\{IMPORTS\\}\\}/g, [...new Set(imports)].sort().join('\\n'))\n .replace(/\\{\\{TRAITS\\}\\}/g, traits.join('\\n'))\n .replace(/\\{\\{DOC_COMMENT\\}\\}/g, docComment)\n .replace(/\\{\\{FILLABLE\\}\\}/g, fillable.join('\\n'))\n .replace(/\\{\\{HIDDEN\\}\\}/g, hidden.join('\\n'))\n .replace(/\\{\\{APPENDS\\}\\}/g, appends.join('\\n'))\n .replace(/\\{\\{CASTS\\}\\}/g, casts.join('\\n'))\n .replace(/\\{\\{RELATIONS\\}\\}/g, relations.join('\\n\\n'));\n\n return {\n path: `${options.baseModelPath}/${className}BaseModel.php`,\n content,\n type: 'entity-base',\n overwrite: true,\n schemaName: schema.name,\n };\n}\n\n/**\n * Find the inverse ManyToOne relationship on target schema that points back to current schema.\n */\nfunction findInverseRelation(\n currentSchemaName: string,\n targetSchemaName: string,\n schemas: SchemaCollection\n): string | null {\n const targetSchema = schemas[targetSchemaName];\n if (!targetSchema || !targetSchema.properties) {\n return null;\n }\n\n for (const [propName, propDef] of Object.entries(targetSchema.properties)) {\n if (propDef.type === 'Association') {\n const assoc = propDef as AssociationDefinition;\n if (assoc.relation === 'ManyToOne' && assoc.target === currentSchemaName) {\n return propName;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Generate relation method.\n */\nfunction generateRelation(\n propName: string,\n assoc: AssociationDefinition,\n schema: LoadedSchema,\n schemas: SchemaCollection,\n options: ResolvedOptions\n): string {\n const methodName = toCamelCase(propName);\n const targetClass = assoc.target ? toPascalCase(assoc.target) : '';\n const fkName = toSnakeCase(propName) + '_id';\n\n switch (assoc.relation) {\n case 'ManyToOne':\n return ` /**\n * Get the ${propName} that owns this model.\n */\n public function ${methodName}(): BelongsTo\n {\n return $this->belongsTo(${targetClass}::class, '${fkName}');\n }`;\n\n case 'OneToOne':\n if (assoc.mappedBy) {\n return ` /**\n * Get the ${propName} for this model.\n */\n public function ${methodName}(): HasOne\n {\n return $this->hasOne(${targetClass}::class, '${toSnakeCase(assoc.mappedBy)}_id');\n }`;\n }\n return ` /**\n * Get the ${propName} that owns this model.\n */\n public function ${methodName}(): BelongsTo\n {\n return $this->belongsTo(${targetClass}::class, '${fkName}');\n }`;\n\n case 'OneToMany': {\n // Find the inverse ManyToOne relationship on target to determine correct FK\n let foreignKey: string;\n if (assoc.inversedBy) {\n // If inversedBy is specified, use it\n foreignKey = toSnakeCase(assoc.inversedBy) + '_id';\n } else if (assoc.target) {\n // Look up the inverse relationship on the target schema\n const inverseRelation = findInverseRelation(schema.name, assoc.target, schemas);\n if (inverseRelation) {\n foreignKey = toSnakeCase(inverseRelation) + '_id';\n } else {\n // Fallback: use the current schema name as snake_case + _id\n foreignKey = toSnakeCase(schema.name) + '_id';\n }\n } else {\n foreignKey = toSnakeCase(propName) + '_id';\n }\n return ` /**\n * Get the ${propName} for this model.\n */\n public function ${methodName}(): HasMany\n {\n return $this->hasMany(${targetClass}::class, '${foreignKey}');\n }`;\n }\n\n case 'ManyToMany': {\n const pivotTable = assoc.joinTable ?? `${toSnakeCase(propName)}_pivot`;\n return ` /**\n * The ${propName} that belong to this model.\n */\n public function ${methodName}(): BelongsToMany\n {\n return $this->belongsToMany(${targetClass}::class, '${pivotTable}')\n ->withTimestamps();\n }`;\n }\n\n case 'MorphTo':\n return ` /**\n * Get the parent ${propName} model.\n */\n public function ${methodName}(): MorphTo\n {\n return $this->morphTo('${methodName}');\n }`;\n\n case 'MorphOne':\n return ` /**\n * Get the ${propName} for this model.\n */\n public function ${methodName}(): MorphOne\n {\n return $this->morphOne(${targetClass}::class, '${assoc.morphName ?? propName}');\n }`;\n\n case 'MorphMany':\n return ` /**\n * Get the ${propName} for this model.\n */\n public function ${methodName}(): MorphMany\n {\n return $this->morphMany(${targetClass}::class, '${assoc.morphName ?? propName}');\n }`;\n\n default:\n return ` // TODO: Implement ${assoc.relation} relation for ${propName}`;\n }\n}\n\n/**\n * Generate file relation method.\n */\nfunction generateFileRelation(propName: string, propDef: { multiple?: boolean }): string {\n const methodName = toCamelCase(propName);\n const relationType = propDef.multiple ? 'MorphMany' : 'MorphOne';\n const relationMethod = propDef.multiple ? 'morphMany' : 'morphOne';\n\n return ` /**\n * Get the ${propName} file(s) for this model.\n */\n public function ${methodName}(): ${relationType}\n {\n return $this->${relationMethod}(FileUpload::class, 'uploadable')\n ->where('attribute_name', '${propName}');\n }`;\n}\n\n/**\n * Generate user model (created once, not overwritten).\n */\nfunction generateEntityModel(\n schema: LoadedSchema,\n options: ResolvedOptions,\n stubContent: string\n): GeneratedModel {\n const className = toPascalCase(schema.name);\n\n const content = stubContent\n .replace(/\\{\\{BASE_MODEL_NAMESPACE\\}\\}/g, options.baseModelNamespace)\n .replace(/\\{\\{MODEL_NAMESPACE\\}\\}/g, options.modelNamespace)\n .replace(/\\{\\{CLASS_NAME\\}\\}/g, className);\n\n return {\n path: `${options.modelPath}/${className}.php`,\n content,\n type: 'entity',\n overwrite: false, // Never overwrite user models\n schemaName: schema.name,\n };\n}\n\n/**\n * Read stub file content.\n */\nfunction getStubContent(stubName: string): string {\n // Stubs are embedded as strings since this runs in Node.js\n const stubs: Record<string, string> = {\n 'base-model': `<?php\n\nnamespace {{BASE_MODEL_NAMESPACE}};\n\n/**\n * Base model class for all Omnify-generated models.\n * Contains model mapping for polymorphic relations.\n *\n * DO NOT EDIT - This file is auto-generated by Omnify.\n * Any changes will be overwritten on next generation.\n *\n * @generated by @famgia/omnify-laravel\n */\n\nuse Illuminate\\\\Database\\\\Eloquent\\\\Model;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation;\n\nabstract class {{BASE_MODEL_CLASS}} extends Model\n{\n /**\n * Model class map for polymorphic relations.\n */\n protected static array $modelMap = [\n{{MODEL_MAP}}\n ];\n\n /**\n * Boot the model and register morph map.\n */\n protected static function boot(): void\n {\n parent::boot();\n\n // Register morph map for polymorphic relations\n Relation::enforceMorphMap(static::$modelMap);\n }\n\n /**\n * Get the model class for a given morph type.\n */\n public static function getModelClass(string $morphType): ?string\n {\n return static::$modelMap[$morphType] ?? null;\n }\n}\n`,\n\n 'entity-base': `<?php\n\nnamespace {{BASE_MODEL_NAMESPACE}};\n\n/**\n * DO NOT EDIT - This file is auto-generated by Omnify.\n * Any changes will be overwritten on next generation.\n *\n * @generated by @famgia/omnify-laravel\n */\n\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsTo;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasOne;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphTo;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphToMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Collection as EloquentCollection;\nuse {{BASE_MODEL_NAMESPACE}}\\\\Traits\\\\HasLocalizedDisplayName;\nuse {{BASE_MODEL_NAMESPACE}}\\\\Locales\\\\{{CLASS_NAME}}Locales;\n{{IMPORTS}}\n\n{{DOC_COMMENT}}\nclass {{CLASS_NAME}}BaseModel extends {{BASE_MODEL_CLASS}}\n{\n use HasLocalizedDisplayName;\n{{TRAITS}}\n /**\n * The table associated with the model.\n */\n protected $table = '{{TABLE_NAME}}';\n\n /**\n * The primary key for the model.\n */\n protected $primaryKey = '{{PRIMARY_KEY}}';\n\n{{KEY_TYPE}}\n{{INCREMENTING}}\n /**\n * Indicates if the model should be timestamped.\n */\n public $timestamps = {{TIMESTAMPS}};\n\n /**\n * Localized display names for this model.\n *\n * @var array<string, string>\n */\n protected static array $localizedDisplayNames = {{CLASS_NAME}}Locales::DISPLAY_NAMES;\n\n /**\n * Localized display names for properties.\n *\n * @var array<string, array<string, string>>\n */\n protected static array $localizedPropertyDisplayNames = {{CLASS_NAME}}Locales::PROPERTY_DISPLAY_NAMES;\n\n /**\n * The attributes that are mass assignable.\n */\n protected $fillable = [\n{{FILLABLE}}\n ];\n\n /**\n * The attributes that should be hidden for serialization.\n */\n protected $hidden = [\n{{HIDDEN}}\n ];\n\n /**\n * The accessors to append to the model's array form.\n */\n protected $appends = [\n{{APPENDS}}\n ];\n\n /**\n * Get the attributes that should be cast.\n */\n protected function casts(): array\n {\n return [\n{{CASTS}}\n ];\n }\n\n{{RELATIONS}}\n}\n`,\n\n 'entity-base-auth': `<?php\n\nnamespace {{BASE_MODEL_NAMESPACE}};\n\n/**\n * DO NOT EDIT - This file is auto-generated by Omnify.\n * Any changes will be overwritten on next generation.\n *\n * @generated by @famgia/omnify-laravel\n */\n\nuse Illuminate\\\\Foundation\\\\Auth\\\\User as Authenticatable;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsTo;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\HasOne;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\BelongsToMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphTo;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphOne;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\MorphToMany;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Collection as EloquentCollection;\nuse Illuminate\\\\Notifications\\\\Notifiable;\nuse {{BASE_MODEL_NAMESPACE}}\\\\Traits\\\\HasLocalizedDisplayName;\nuse {{BASE_MODEL_NAMESPACE}}\\\\Locales\\\\{{CLASS_NAME}}Locales;\n{{IMPORTS}}\n\n{{DOC_COMMENT}}\nclass {{CLASS_NAME}}BaseModel extends Authenticatable\n{\n use Notifiable;\n use HasLocalizedDisplayName;\n{{TRAITS}}\n /**\n * The table associated with the model.\n */\n protected $table = '{{TABLE_NAME}}';\n\n /**\n * The primary key for the model.\n */\n protected $primaryKey = '{{PRIMARY_KEY}}';\n\n{{KEY_TYPE}}\n{{INCREMENTING}}\n /**\n * Indicates if the model should be timestamped.\n */\n public $timestamps = {{TIMESTAMPS}};\n\n /**\n * Localized display names for this model.\n *\n * @var array<string, string>\n */\n protected static array $localizedDisplayNames = {{CLASS_NAME}}Locales::DISPLAY_NAMES;\n\n /**\n * Localized display names for properties.\n *\n * @var array<string, array<string, string>>\n */\n protected static array $localizedPropertyDisplayNames = {{CLASS_NAME}}Locales::PROPERTY_DISPLAY_NAMES;\n\n /**\n * The attributes that are mass assignable.\n */\n protected $fillable = [\n{{FILLABLE}}\n ];\n\n /**\n * The attributes that should be hidden for serialization.\n */\n protected $hidden = [\n{{HIDDEN}}\n ];\n\n /**\n * The accessors to append to the model's array form.\n */\n protected $appends = [\n{{APPENDS}}\n ];\n\n /**\n * Get the attributes that should be cast.\n */\n protected function casts(): array\n {\n return [\n{{CASTS}}\n ];\n }\n\n{{RELATIONS}}\n}\n`,\n\n 'entity': `<?php\n\nnamespace {{MODEL_NAMESPACE}};\n\nuse {{BASE_MODEL_NAMESPACE}}\\\\{{CLASS_NAME}}BaseModel;\nuse Illuminate\\\\Database\\\\Eloquent\\\\Factories\\\\HasFactory;\n\n/**\n * {{CLASS_NAME}} Model\n *\n * This file is generated once and can be customized.\n * Add your custom methods and logic here.\n */\nclass {{CLASS_NAME}} extends {{CLASS_NAME}}BaseModel\n{\n use HasFactory;\n\n /**\n * Create a new model instance.\n */\n public function __construct(array $attributes = [])\n {\n parent::__construct($attributes);\n }\n\n // Add your custom methods here\n}\n`,\n\n 'service-provider': `<?php\n\nnamespace App\\\\Providers;\n\nuse Illuminate\\\\Database\\\\Eloquent\\\\Relations\\\\Relation;\nuse Illuminate\\\\Support\\\\ServiceProvider;\n\n/**\n * Omnify Service Provider\n *\n * DO NOT EDIT - This file is auto-generated by Omnify.\n * Any changes will be overwritten on next generation.\n *\n * - Loads Omnify migrations from database/migrations/omnify\n * - Registers morph map for polymorphic relationships\n *\n * @generated by @famgia/omnify-laravel\n */\nclass OmnifyServiceProvider extends ServiceProvider\n{\n /**\n * Register any application services.\n */\n public function register(): void\n {\n //\n }\n\n /**\n * Bootstrap any application services.\n */\n public function boot(): void\n {\n // Load Omnify migrations from custom directory\n $this->loadMigrationsFrom(database_path('migrations/omnify'));\n\n // Register morph map for polymorphic relationships\n Relation::enforceMorphMap([\n{{MORPH_MAP}}\n ]);\n }\n}\n`,\n\n 'has-localized-display-name': `<?php\n\nnamespace {{BASE_MODEL_NAMESPACE}}\\\\Traits;\n\n/**\n * Trait for localized display names.\n * Uses Laravel's app()->getLocale() for locale resolution.\n *\n * DO NOT EDIT - This file is auto-generated by Omnify.\n * Any changes will be overwritten on next generation.\n *\n * @generated by @famgia/omnify-laravel\n */\ntrait HasLocalizedDisplayName\n{\n /**\n * Get the localized display name for this model.\n *\n * @param string|null $locale Locale code (defaults to app locale)\n * @return string\n */\n public static function displayName(?string $locale = null): string\n {\n $locale = $locale ?? app()->getLocale();\n $displayNames = static::$localizedDisplayNames ?? [];\n\n return $displayNames[$locale]\n ?? $displayNames[config('app.fallback_locale', 'en')]\n ?? $displayNames[array_key_first($displayNames) ?? 'en']\n ?? class_basename(static::class);\n }\n\n /**\n * Get all localized display names for this model.\n *\n * @return array<string, string>\n */\n public static function allDisplayNames(): array\n {\n return static::$localizedDisplayNames ?? [];\n }\n\n /**\n * Get the localized display name for a property.\n *\n * @param string $property Property name\n * @param string|null $locale Locale code (defaults to app locale)\n * @return string\n */\n public static function propertyDisplayName(string $property, ?string $locale = null): string\n {\n $locale = $locale ?? app()->getLocale();\n $displayNames = static::$localizedPropertyDisplayNames[$property] ?? [];\n\n return $displayNames[$locale]\n ?? $displayNames[config('app.fallback_locale', 'en')]\n ?? $displayNames[array_key_first($displayNames) ?? 'en']\n ?? $property;\n }\n\n /**\n * Get all localized display names for a property.\n *\n * @param string $property Property name\n * @return array<string, string>\n */\n public static function allPropertyDisplayNames(string $property): array\n {\n return static::$localizedPropertyDisplayNames[$property] ?? [];\n }\n\n /**\n * Get all property display names for a given locale.\n *\n * @param string|null $locale Locale code (defaults to app locale)\n * @return array<string, string>\n */\n public static function allPropertyDisplayNamesForLocale(?string $locale = null): array\n {\n $locale = $locale ?? app()->getLocale();\n $result = [];\n\n foreach (static::$localizedPropertyDisplayNames ?? [] as $property => $displayNames) {\n $result[$property] = $displayNames[$locale]\n ?? $displayNames[config('app.fallback_locale', 'en')]\n ?? $displayNames[array_key_first($displayNames) ?? 'en']\n ?? $property;\n }\n\n return $result;\n }\n}\n`,\n\n 'locales': `<?php\n\nnamespace {{BASE_MODEL_NAMESPACE}}\\\\Locales;\n\n/**\n * Localized display names for {{CLASS_NAME}}.\n *\n * DO NOT EDIT - This file is auto-generated by Omnify.\n * Any changes will be overwritten on next generation.\n *\n * @generated by @famgia/omnify-laravel\n */\nclass {{CLASS_NAME}}Locales\n{\n /**\n * Localized display names for the model.\n *\n * @var array<string, string>\n */\n public const DISPLAY_NAMES = [\n{{LOCALIZED_DISPLAY_NAMES}}\n ];\n\n /**\n * Localized display names for properties.\n *\n * @var array<string, array<string, string>>\n */\n public const PROPERTY_DISPLAY_NAMES = [\n{{LOCALIZED_PROPERTY_DISPLAY_NAMES}}\n ];\n}\n`,\n };\n\n return stubs[stubName] ?? '';\n}\n\n/**\n * Generate OmnifyServiceProvider with morph map.\n */\nfunction generateServiceProvider(\n schemas: SchemaCollection,\n options: ResolvedOptions,\n stubContent: string\n): GeneratedModel {\n // Build morph map - only include models (not enums)\n const morphMap = Object.values(schemas)\n .filter(s => s.kind !== 'enum')\n .map(s => {\n const className = toPascalCase(s.name);\n return ` '${s.name}' => \\\\${options.modelNamespace}\\\\${className}::class,`;\n })\n .join('\\n');\n\n const content = stubContent\n .replace(/\\{\\{MORPH_MAP\\}\\}/g, morphMap);\n\n return {\n path: `${options.providersPath}/OmnifyServiceProvider.php`,\n content,\n type: 'service-provider',\n overwrite: true, // Always overwrite to keep morph map in sync\n schemaName: '__service_provider__',\n };\n}\n\n/**\n * Generate the HasLocalizedDisplayName trait.\n */\nfunction generateLocalizedDisplayNameTrait(\n options: ResolvedOptions,\n stubContent: string\n): GeneratedModel {\n const content = stubContent\n .replace(/\\{\\{BASE_MODEL_NAMESPACE\\}\\}/g, options.baseModelNamespace);\n\n return {\n path: `${options.baseModelPath}/Traits/HasLocalizedDisplayName.php`,\n content,\n type: 'trait',\n overwrite: true, // Always overwrite trait\n schemaName: '__trait__',\n };\n}\n\n/**\n * Generate the Locales class for a schema.\n */\nfunction generateLocalesClass(\n schema: LoadedSchema,\n options: ResolvedOptions,\n stubContent: string\n): GeneratedModel {\n const className = toPascalCase(schema.name);\n\n // Generate localized display names\n const localizedDisplayNames = generateLocalizedDisplayNames(schema.displayName);\n const localizedPropertyDisplayNames = generatePropertyLocalizedDisplayNames(schema);\n\n const content = stubContent\n .replace(/\\{\\{BASE_MODEL_NAMESPACE\\}\\}/g, options.baseModelNamespace)\n .replace(/\\{\\{CLASS_NAME\\}\\}/g, className)\n .replace(/\\{\\{LOCALIZED_DISPLAY_NAMES\\}\\}/g, localizedDisplayNames)\n .replace(/\\{\\{LOCALIZED_PROPERTY_DISPLAY_NAMES\\}\\}/g, localizedPropertyDisplayNames);\n\n return {\n path: `${options.baseModelPath}/Locales/${className}Locales.php`,\n content,\n type: 'locales',\n overwrite: true, // Always overwrite locales\n schemaName: schema.name,\n };\n}\n\n/**\n * Generate all models for the given schemas.\n */\nexport function generateModels(\n schemas: SchemaCollection,\n options?: ModelGeneratorOptions\n): GeneratedModel[] {\n const resolved = resolveOptions(options);\n const models: GeneratedModel[] = [];\n\n // Generate shared base model\n models.push(generateBaseModel(schemas, resolved, getStubContent('base-model')));\n\n // Generate HasLocalizedDisplayName trait in Traits subfolder\n models.push(generateLocalizedDisplayNameTrait(resolved, getStubContent('has-localized-display-name')));\n\n // Generate OmnifyServiceProvider with morph map\n models.push(generateServiceProvider(schemas, resolved, getStubContent('service-provider')));\n\n // Generate models for each schema (excluding enums)\n for (const schema of Object.values(schemas)) {\n if (schema.kind === 'enum') {\n continue;\n }\n\n // Generate Locales class in Locales subfolder\n models.push(generateLocalesClass(schema, resolved, getStubContent('locales')));\n\n // Generate entity base model (always overwritten)\n models.push(generateEntityBaseModel(\n schema,\n schemas,\n resolved,\n getStubContent('entity-base'),\n getStubContent('entity-base-auth')\n ));\n\n // Generate user model (created once)\n models.push(generateEntityModel(schema, resolved, getStubContent('entity')));\n }\n\n return models;\n}\n\n/**\n * Get the output path for a model.\n */\nexport function getModelPath(model: GeneratedModel): string {\n return model.path;\n}\n\n/**\n * Generate provider registration for Laravel.\n * Handles both Laravel 11+ (bootstrap/providers.php) and Laravel 10- (config/app.php).\n *\n * @param existingContent - Existing file content (null if file doesn't exist)\n * @param laravelVersion - Laravel version type\n * @returns Registration result or null if already registered\n */\nexport function generateProviderRegistration(\n existingContent: string | null,\n laravelVersion: 'laravel11+' | 'laravel10-'\n): ProviderRegistrationResult | null {\n const providerClass = 'App\\\\Providers\\\\OmnifyServiceProvider::class';\n const providerLine = ` ${providerClass},`;\n\n // Check if already registered\n if (existingContent && existingContent.includes('OmnifyServiceProvider')) {\n return {\n path: laravelVersion === 'laravel11+' ? 'bootstrap/providers.php' : 'config/app.php',\n content: existingContent,\n laravelVersion,\n alreadyRegistered: true,\n };\n }\n\n if (laravelVersion === 'laravel11+') {\n // Laravel 11+ uses bootstrap/providers.php\n if (existingContent) {\n // Find the closing bracket of the array and insert before it\n const lines = existingContent.split('\\n');\n const result: string[] = [];\n let inserted = false;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n // Find the closing ];\n if (!inserted && line.trim() === '];') {\n // Insert before closing bracket\n result.push(providerLine);\n inserted = true;\n }\n result.push(line);\n }\n\n return {\n path: 'bootstrap/providers.php',\n content: result.join('\\n'),\n laravelVersion,\n alreadyRegistered: false,\n };\n } else {\n // Create new file\n return {\n path: 'bootstrap/providers.php',\n content: `<?php\n\nreturn [\n App\\\\Providers\\\\AppServiceProvider::class,\n${providerLine}\n];\n`,\n laravelVersion,\n alreadyRegistered: false,\n };\n }\n } else {\n // Laravel 10- uses config/app.php\n if (existingContent) {\n // Find 'providers' => [...] and insert OmnifyServiceProvider\n // This is more complex because config/app.php has multiple arrays\n\n // Look for the providers array closing\n // Pattern: After \"App\\Providers\\...\" look for the next line with ], and insert before\n const providersSectionRegex = /'providers'\\s*=>\\s*\\[[\\s\\S]*?\\n(\\s*)\\]/m;\n const match = existingContent.match(providersSectionRegex);\n\n if (match) {\n // Find position to insert - before the closing ]\n const providersStart = existingContent.indexOf(\"'providers'\");\n if (providersStart === -1) {\n return null; // Can't find providers section\n }\n\n // Find the closing ] of providers array\n let depth = 0;\n let foundStart = false;\n let insertPos = -1;\n\n for (let i = providersStart; i < existingContent.length; i++) {\n const char = existingContent[i];\n if (char === '[') {\n foundStart = true;\n depth++;\n } else if (char === ']') {\n depth--;\n if (foundStart && depth === 0) {\n insertPos = i;\n break;\n }\n }\n }\n\n if (insertPos !== -1) {\n // Find the last provider line before closing ]\n const beforeClose = existingContent.substring(0, insertPos);\n const lastNewline = beforeClose.lastIndexOf('\\n');\n\n // Insert after last provider, before ]\n const content =\n existingContent.substring(0, lastNewline + 1) +\n providerLine + '\\n' +\n existingContent.substring(lastNewline + 1);\n\n return {\n path: 'config/app.php',\n content,\n laravelVersion,\n alreadyRegistered: false,\n };\n }\n }\n\n return null; // Couldn't parse config/app.php\n } else {\n // Can't create config/app.php from scratch - it's too complex\n return null;\n }\n }\n}\n","/**\n * Utility functions for Laravel generator.\n */\n\n/**\n * Convert a string to snake_case.\n */\nexport function toSnakeCase(str: string): string {\n return str\n .replace(/([A-Z])/g, '_$1')\n .replace(/^_/, '')\n .toLowerCase();\n}\n\n/**\n * Convert a string to PascalCase.\n */\nexport function toPascalCase(str: string): string {\n return str\n .replace(/[-_](.)/g, (_, c) => c.toUpperCase())\n .replace(/^(.)/, (_, c) => c.toUpperCase());\n}\n\n/**\n * Convert a string to camelCase.\n */\nexport function toCamelCase(str: string): string {\n const pascal = toPascalCase(str);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/**\n * Simple pluralization (English).\n */\nexport function pluralize(word: string): string {\n if (word.endsWith('y') && !['ay', 'ey', 'iy', 'oy', 'uy'].some(v => word.endsWith(v))) {\n return word.slice(0, -1) + 'ies';\n }\n if (word.endsWith('s') || word.endsWith('x') || word.endsWith('z') ||\n word.endsWith('ch') || word.endsWith('sh')) {\n return word + 'es';\n }\n return word + 's';\n}\n","/**\n * Laravel Factory Generator\n *\n * Generates Laravel factory files for Eloquent models.\n */\n\nimport type { SchemaCollection, LoadedSchema, PropertyDefinition } from '@famgia/omnify-types';\nimport { toPascalCase, toSnakeCase } from '../utils.js';\n\n/**\n * Options for factory generation.\n */\nexport interface FactoryGeneratorOptions {\n /** Model namespace */\n modelNamespace?: string;\n /** Factory output path */\n factoryPath?: string;\n /** Faker locale */\n fakerLocale?: string;\n}\n\n/**\n * Generated factory output.\n */\nexport interface GeneratedFactory {\n /** Factory class name */\n name: string;\n /** Schema name this factory is for */\n schemaName: string;\n /** Output path for the factory file */\n path: string;\n /** Generated factory content */\n content: string;\n /** Whether to overwrite if exists */\n overwrite: boolean;\n}\n\n/**\n * Resolved options with defaults applied.\n */\ninterface ResolvedOptions {\n modelNamespace: string;\n factoryPath: string;\n fakerLocale: string;\n}\n\n/**\n * Resolves options with defaults.\n */\nfunction resolveOptions(options?: FactoryGeneratorOptions): ResolvedOptions {\n return {\n modelNamespace: options?.modelNamespace ?? 'App\\\\Models',\n factoryPath: options?.factoryPath ?? 'database/factories',\n fakerLocale: options?.fakerLocale ?? 'en_US',\n };\n}\n\n/**\n * Gets the factory stub content.\n */\nfunction getStubContent(): string {\n return `<?php\n\nnamespace Database\\\\Factories;\n\nuse {{MODEL_NAMESPACE}}\\\\{{MODEL_NAME}};\nuse Illuminate\\\\Database\\\\Eloquent\\\\Factories\\\\Factory;\n{{IMPORTS}}\n\n/**\n * @extends Factory<{{MODEL_NAME}}>\n */\nclass {{MODEL_NAME}}Factory extends Factory\n{\n protected $model = {{MODEL_NAME}}::class;\n\n /**\n * Define the model's default state.\n *\n * @return array<string, mixed>\n */\n public function definition(): array\n {\n return [\n{{ATTRIBUTES}}\n ];\n }\n}\n`;\n}\n\n/**\n * Generates factory fake data for a property based on its type and name.\n */\nfunction generateFakeData(\n propertyName: string,\n property: PropertyDefinition,\n schema: LoadedSchema,\n schemas: SchemaCollection\n): string | null {\n const type = property.type;\n\n // Skip system fields\n if (['deleted_at', 'created_at', 'updated_at'].includes(propertyName)) {\n return null;\n }\n\n // Skip association properties (foreign keys are handled separately)\n if (type === 'Association') {\n return null;\n }\n\n // Handle different property types\n switch (type) {\n case 'String':\n return generateStringFake(propertyName, property);\n\n case 'Email':\n return `'${propertyName}' => fake()->unique()->safeEmail(),`;\n\n case 'Password':\n return `'${propertyName}' => bcrypt('password'),`;\n\n case 'Int':\n case 'BigInt':\n return generateIntFake(propertyName, property);\n\n case 'Float':\n case 'Decimal':\n return `'${propertyName}' => fake()->randomFloat(2, 1, 10000),`;\n\n case 'Boolean':\n return `'${propertyName}' => fake()->boolean(),`;\n\n case 'Text':\n return `'${propertyName}' => fake()->paragraphs(3, true),`;\n\n case 'LongText':\n return `'${propertyName}' => fake()->paragraphs(5, true),`;\n\n case 'Date':\n return `'${propertyName}' => fake()->date(),`;\n\n case 'Time':\n return `'${propertyName}' => fake()->time(),`;\n\n case 'Timestamp':\n case 'DateTime':\n return `'${propertyName}' => fake()->dateTime(),`;\n\n case 'Json':\n return `'${propertyName}' => [],`;\n\n case 'Enum':\n return generateEnumFake(propertyName, property);\n\n case 'EnumRef':\n return generateEnumRefFake(propertyName, property, schemas);\n\n default:\n // Default to sentence for unknown types\n return `'${propertyName}' => fake()->sentence(),`;\n }\n}\n\n/**\n * Generates fake data for String type based on property name patterns.\n */\nfunction generateStringFake(propertyName: string, property: PropertyDefinition): string {\n // Handle special field names\n if (propertyName === 'slug') {\n return `'${propertyName}' => fake()->unique()->slug(),`;\n }\n\n if (propertyName === 'uuid' || propertyName === 'uid') {\n return `'${propertyName}' => (string) \\\\Illuminate\\\\Support\\\\Str::uuid(),`;\n }\n\n if (propertyName.includes('email')) {\n return `'${propertyName}' => fake()->unique()->safeEmail(),`;\n }\n\n if (propertyName.includes('phone')) {\n return `'${propertyName}' => fake()->phoneNumber(),`;\n }\n\n // Check image/photo/avatar before url to handle cases like 'avatar_url'\n if (propertyName.includes('image') || propertyName.includes('photo') || propertyName.includes('avatar')) {\n return `'${propertyName}' => fake()->imageUrl(),`;\n }\n\n if (propertyName.includes('url') || propertyName.includes('website')) {\n return `'${propertyName}' => fake()->url(),`;\n }\n\n if (propertyName.includes('path') || propertyName.includes('file')) {\n return `'${propertyName}' => 'uploads/' . fake()->uuid() . '.jpg',`;\n }\n\n if (propertyName === 'name' || propertyName === 'title') {\n return `'${propertyName}' => fake()->sentence(3),`;\n }\n\n if (propertyName.includes('name')) {\n return `'${propertyName}' => fake()->name(),`;\n }\n\n if (propertyName.includes('address')) {\n return `'${propertyName}' => fake()->address(),`;\n }\n\n if (propertyName.includes('city')) {\n return `'${propertyName}' => fake()->city(),`;\n }\n\n if (propertyName.includes('country')) {\n return `'${propertyName}' => fake()->country(),`;\n }\n\n if (propertyName.includes('zip') || propertyName.includes('postal')) {\n return `'${propertyName}' => fake()->postcode(),`;\n }\n\n if (propertyName.includes('color')) {\n return `'${propertyName}' => fake()->hexColor(),`;\n }\n\n if (propertyName.includes('token') || propertyName.includes('secret') || propertyName.includes('key')) {\n return `'${propertyName}' => \\\\Illuminate\\\\Support\\\\Str::random(32),`;\n }\n\n if (propertyName.includes('code')) {\n return `'${propertyName}' => fake()->unique()->regexify('[A-Z0-9]{8}'),`;\n }\n\n // Default string\n const length = (property as { length?: number }).length;\n if (length && length <= 50) {\n return `'${propertyName}' => fake()->words(3, true),`;\n }\n\n return `'${propertyName}' => fake()->sentence(),`;\n}\n\n/**\n * Generates fake data for Integer types.\n */\nfunction generateIntFake(propertyName: string, property: PropertyDefinition): string {\n if (propertyName.includes('count') || propertyName.includes('quantity')) {\n return `'${propertyName}' => fake()->numberBetween(0, 100),`;\n }\n\n if (propertyName.includes('price') || propertyName.includes('amount') || propertyName.includes('cost')) {\n return `'${propertyName}' => fake()->numberBetween(100, 10000),`;\n }\n\n if (propertyName.includes('order') || propertyName.includes('sort') || propertyName.includes('position')) {\n return `'${propertyName}' => fake()->numberBetween(1, 100),`;\n }\n\n if (propertyName.includes('age')) {\n return `'${propertyName}' => fake()->numberBetween(18, 80),`;\n }\n\n if (propertyName.includes('year')) {\n return `'${propertyName}' => fake()->year(),`;\n }\n\n return `'${propertyName}' => fake()->numberBetween(1, 1000),`;\n}\n\n/**\n * Generates fake data for inline Enum type.\n */\nfunction generateEnumFake(propertyName: string, property: PropertyDefinition): string {\n const enumValues = (property as { enum?: readonly (string | { value: string })[] }).enum;\n if (!enumValues || enumValues.length === 0) {\n return `'${propertyName}' => null,`;\n }\n\n // Extract values (handle both string and object formats)\n const values = enumValues.map(v => typeof v === 'string' ? v : v.value);\n const valuesStr = values.map(v => `'${v}'`).join(', ');\n\n return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;\n}\n\n/**\n * Generates fake data for EnumRef type.\n */\nfunction generateEnumRefFake(\n propertyName: string,\n property: PropertyDefinition,\n schemas: SchemaCollection\n): string {\n const enumName = (property as { enum?: string }).enum;\n if (!enumName) {\n return `'${propertyName}' => null,`;\n }\n\n const enumSchema = schemas[enumName];\n if (!enumSchema || enumSchema.kind !== 'enum' || !enumSchema.values) {\n return `'${propertyName}' => null,`;\n }\n\n const valuesStr = enumSchema.values.map(v => `'${v}'`).join(', ');\n return `'${propertyName}' => fake()->randomElement([${valuesStr}]),`;\n}\n\n/**\n * Generates factory data for association (foreign key).\n */\nfunction generateAssociationFake(\n propertyName: string,\n property: PropertyDefinition,\n schema: LoadedSchema,\n schemas: SchemaCollection,\n modelNamespace: string\n): { fake: string; import?: string } | null {\n if (property.type !== 'Association') {\n return null;\n }\n\n const relation = (property as { relation?: string }).relation;\n const target = (property as { target?: string }).target;\n\n // Only handle ManyToOne (belongsTo) relationships\n if (relation !== 'ManyToOne' || !target) {\n return null;\n }\n\n const foreignKey = `${toSnakeCase(propertyName)}_id`;\n const isNullable = (property as { nullable?: boolean }).nullable ?? false;\n const targetSchema = schemas[target];\n\n // Check if target schema exists\n if (!targetSchema) {\n return null;\n }\n\n // Generate the fake data\n let fake: string;\n if (isNullable) {\n fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id,`;\n } else {\n fake = `'${foreignKey}' => ${target}::query()->inRandomOrder()->first()?->id ?? ${target}::factory()->create()->id,`;\n }\n\n // Add import if target is different from current schema\n let importStatement: string | undefined;\n if (target !== schema.name) {\n importStatement = `use ${modelNamespace}\\\\${target};`;\n }\n\n return { fake, import: importStatement };\n}\n\n/**\n * Generates a factory for a single schema.\n */\nfunction generateFactory(\n schema: LoadedSchema,\n schemas: SchemaCollection,\n options: ResolvedOptions,\n stubContent: string\n): GeneratedFactory | null {\n // Skip enum schemas\n if (schema.kind === 'enum') {\n return null;\n }\n\n const modelName = toPascalCase(schema.name);\n const factoryName = `${modelName}Factory`;\n\n const attributes: string[] = [];\n const imports: string[] = [];\n\n // Process properties\n if (schema.properties) {\n for (const [propName, prop] of Object.entries(schema.properties)) {\n // Handle associations (foreign keys)\n if (prop.type === 'Association') {\n const assocResult = generateAssociationFake(propName, prop, schema, schemas, options.modelNamespace);\n if (assocResult) {\n attributes.push(assocResult.fake);\n if (assocResult.import) {\n imports.push(assocResult.import);\n }\n }\n continue;\n }\n\n // Handle regular properties\n const fake = generateFakeData(propName, prop, schema, schemas);\n if (fake) {\n attributes.push(fake);\n }\n }\n }\n\n // Build the factory content\n let content = stubContent;\n content = content.replace(/\\{\\{MODEL_NAMESPACE\\}\\}/g, options.modelNamespace);\n content = content.replace(/\\{\\{MODEL_NAME\\}\\}/g, modelName);\n\n // Format imports\n const uniqueImports = [...new Set(imports)];\n const importsStr = uniqueImports.length > 0\n ? '\\n' + uniqueImports.join('\\n')\n : '';\n content = content.replace(/\\{\\{IMPORTS\\}\\}/g, importsStr);\n\n // Format attributes with proper indentation\n const attributesStr = attributes.length > 0\n ? attributes.map(a => ` ${a}`).join('\\n')\n : '';\n content = content.replace(/\\{\\{ATTRIBUTES\\}\\}/g, attributesStr);\n\n return {\n name: factoryName,\n schemaName: schema.name,\n path: `${options.factoryPath}/${factoryName}.php`,\n content,\n overwrite: false, // Factories should not overwrite existing files\n };\n}\n\n/**\n * Generates factories for all schemas.\n */\nexport function generateFactories(\n schemas: SchemaCollection,\n options?: FactoryGeneratorOptions\n): GeneratedFactory[] {\n const resolved = resolveOptions(options);\n const stubContent = getStubContent();\n const factories: GeneratedFactory[] = [];\n\n for (const schema of Object.values(schemas)) {\n const factory = generateFactory(schema, schemas, resolved, stubContent);\n if (factory) {\n factories.push(factory);\n }\n }\n\n return factories;\n}\n\n/**\n * Gets the output path for a factory.\n */\nexport function getFactoryPath(factory: GeneratedFactory): string {\n return factory.path;\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkGpB,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA6BV,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA4CpB,8BAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA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","names":["isNullable","columnMethod","TYPE_METHOD_MAP","generateTimestamp","hasAutoId","properties","typeDef","isCompoundType","resolveOptions","getStubContent","isNullable","resolveOptions"]}
package/dist/index.cjs CHANGED
@@ -1268,6 +1268,7 @@ var DEFAULT_OPTIONS = {
1268
1268
  baseModelClassName: "BaseModel",
1269
1269
  baseModelPath: "app/Models/OmnifyBase",
1270
1270
  modelPath: "app/Models",
1271
+ providersPath: "app/Providers",
1271
1272
  customTypes: /* @__PURE__ */ new Map()
1272
1273
  };
1273
1274
  function generateLocalizedDisplayNames(displayName, indent = " ") {
@@ -1316,6 +1317,7 @@ function resolveOptions(options) {
1316
1317
  baseModelClassName: options?.baseModelClassName ?? DEFAULT_OPTIONS.baseModelClassName,
1317
1318
  baseModelPath: options?.baseModelPath ?? DEFAULT_OPTIONS.baseModelPath,
1318
1319
  modelPath: options?.modelPath ?? DEFAULT_OPTIONS.modelPath,
1320
+ providersPath: options?.providersPath ?? DEFAULT_OPTIONS.providersPath,
1319
1321
  customTypes: options?.customTypes ?? /* @__PURE__ */ new Map()
1320
1322
  };
1321
1323
  }
@@ -2150,7 +2152,7 @@ function generateServiceProvider(schemas, options, stubContent) {
2150
2152
  }).join("\n");
2151
2153
  const content = stubContent.replace(/\{\{MORPH_MAP\}\}/g, morphMap);
2152
2154
  return {
2153
- path: "app/Providers/OmnifyServiceProvider.php",
2155
+ path: `${options.providersPath}/OmnifyServiceProvider.php`,
2154
2156
  content,
2155
2157
  type: "service-provider",
2156
2158
  overwrite: true,
@@ -2607,6 +2609,14 @@ var LARAVEL_CONFIG_SCHEMA = {
2607
2609
  default: "app/Models/OmnifyBase",
2608
2610
  group: "output"
2609
2611
  },
2612
+ {
2613
+ key: "providersPath",
2614
+ type: "path",
2615
+ label: "Providers Path",
2616
+ description: "Directory for Laravel service provider files",
2617
+ default: "app/Providers",
2618
+ group: "output"
2619
+ },
2610
2620
  {
2611
2621
  key: "generateModels",
2612
2622
  type: "boolean",
@@ -2646,6 +2656,7 @@ function resolveOptions3(options) {
2646
2656
  migrationsPath: options?.migrationsPath ?? "database/migrations/omnify",
2647
2657
  modelsPath: options?.modelsPath ?? "app/Models",
2648
2658
  baseModelsPath: options?.baseModelsPath ?? "app/Models/OmnifyBase",
2659
+ providersPath: options?.providersPath ?? "app/Providers",
2649
2660
  modelNamespace: options?.modelNamespace ?? "App\\Models",
2650
2661
  baseModelNamespace: options?.baseModelNamespace ?? "App\\Models\\OmnifyBase",
2651
2662
  generateModels: options?.generateModels ?? true,
@@ -2750,6 +2761,7 @@ function laravelPlugin(options) {
2750
2761
  baseModelNamespace: resolved.baseModelNamespace,
2751
2762
  modelPath: resolved.modelsPath,
2752
2763
  baseModelPath: resolved.baseModelsPath,
2764
+ providersPath: resolved.providersPath,
2753
2765
  customTypes: ctx.customTypes
2754
2766
  };
2755
2767
  const models = generateModels(ctx.schemas, modelOptions);