@famgia/omnify-laravel 0.0.42 → 0.0.44

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.
@@ -1357,8 +1357,12 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1357
1357
  const properties = schema.properties ?? {};
1358
1358
  for (const [propName, propDef] of Object.entries(properties)) {
1359
1359
  const snakeName = toSnakeCase(propName);
1360
- const phpType = getPhpDocType(propDef, schemas);
1361
- docProperties.push(` * @property ${phpType} $${snakeName}`);
1360
+ const typeDef = options.customTypes.get(propDef.type);
1361
+ const isCompoundType = typeDef?.compound && typeDef.expand;
1362
+ if (!isCompoundType) {
1363
+ const phpType = getPhpDocType(propDef, schemas);
1364
+ docProperties.push(` * @property ${phpType} $${snakeName}`);
1365
+ }
1362
1366
  if (propDef.type === "Association") {
1363
1367
  const assoc = propDef;
1364
1368
  if (assoc.target) {
@@ -1389,14 +1393,19 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1389
1393
  const propWithOptions = propDef;
1390
1394
  const isFillable = propWithOptions.fillable !== false;
1391
1395
  const isHidden = propWithOptions.hidden === true;
1392
- const typeDef = options.customTypes.get(propDef.type);
1393
- const isCompoundType = typeDef?.compound && typeDef.expand;
1394
- if (isCompoundType && typeDef.expand) {
1396
+ const typeDef2 = options.customTypes.get(propDef.type);
1397
+ const isCompoundType2 = typeDef2?.compound && typeDef2.expand;
1398
+ if (isCompoundType2 && typeDef2.expand) {
1395
1399
  const fieldOverrides = propWithOptions.fields ?? {};
1396
- for (const field of typeDef.expand) {
1400
+ const basePropWithNullable = propDef;
1401
+ for (const field of typeDef2.expand) {
1397
1402
  const suffixSnake = toSnakeCase(field.suffix);
1398
1403
  const fieldName = `${snakeName}_${suffixSnake}`;
1399
1404
  const override = fieldOverrides[field.suffix];
1405
+ const fieldNullable = override?.nullable ?? basePropWithNullable.nullable ?? false;
1406
+ const phpType = field.typescript?.type === "number" ? "int" : "string";
1407
+ const nullSuffix = fieldNullable ? "|null" : "";
1408
+ docProperties.push(` * @property ${phpType}${nullSuffix} $${fieldName}`);
1400
1409
  const fieldFillable = override?.fillable !== void 0 ? override.fillable : isFillable;
1401
1410
  if (fieldFillable) {
1402
1411
  fillable.push(` '${fieldName}',`);
@@ -1418,8 +1427,8 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1418
1427
  hidden.push(` '${snakeName}',`);
1419
1428
  }
1420
1429
  }
1421
- if (typeDef?.compound && typeDef.accessors) {
1422
- for (const accessor of typeDef.accessors) {
1430
+ if (typeDef2?.compound && typeDef2.accessors) {
1431
+ for (const accessor of typeDef2.accessors) {
1423
1432
  const accessorName = `${snakeName}_${toSnakeCase(accessor.name)}`;
1424
1433
  appends.push(` '${accessorName}',`);
1425
1434
  const methodName = toPascalCase(accessorName);
@@ -2763,4 +2772,4 @@ export {
2763
2772
  generateProviderRegistration,
2764
2773
  laravelPlugin
2765
2774
  };
2766
- //# sourceMappingURL=chunk-4FPVRQVO.js.map
2775
+ //# sourceMappingURL=chunk-AF6XW6JU.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: '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 * 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 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 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 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 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 };\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 // 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 };\n\n // Only handle MorphTo - it creates the type and id columns\n if (assocProp.relation !== 'MorphTo') {\n return null;\n }\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: [{ 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: [{ 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 return {\n tableName,\n columns,\n primaryKey: ['id'],\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 Int: 'integer',\n BigInt: 'bigInteger',\n Float: 'double',\n Decimal: 'decimal',\n Boolean: 'boolean',\n Text: 'text',\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 * 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 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 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 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 const primaryKey = 'id';\n const idType = schema.options?.idType ?? 'BigInt';\n const isUuid = idType === 'Uuid';\n const isStringKey = idType === 'Uuid' || idType === 'String';\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 = isUuid ? ` /**\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: 'app/Providers/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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AAAA;AAAA;AAAA,IA+Ff,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;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;AAAA;AAAA;AAAA,IA8F9B,WAAW;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,EAiCb;AAEA,SAAO,MAAM,QAAQ,KAAK;AAC5B;AAKA,SAAS,wBACP,SACA,SACA,aACgB;AAEhB,QAAM,WAAW,OAAO,OAAO,OAAO,EACnC,OAAO,OAAK,EAAE,SAAS,MAAM,EAC7B,IAAI,OAAK;AACR,UAAM,YAAY,aAAa,EAAE,IAAI;AACrC,WAAO,gBAAgB,EAAE,IAAI,UAAU,QAAQ,cAAc,KAAK,SAAS;AAAA,EAC7E,CAAC,EACA,KAAK,IAAI;AAEZ,QAAM,UAAU,YACb,QAAQ,sBAAsB,QAAQ;AAEzC,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,MAAM;AAAA,IACN,WAAW;AAAA;AAAA,IACX,YAAY;AAAA,EACd;AACF;AAKA,SAAS,kCACP,SACA,aACgB;AAChB,QAAM,UAAU,YACb,QAAQ,iCAAiC,QAAQ,kBAAkB;AAEtE,SAAO;AAAA,IACL,MAAM,GAAG,QAAQ,aAAa;AAAA,IAC9B;AAAA,IACA,MAAM;AAAA,IACN,WAAW;AAAA;AAAA,IACX,YAAY;AAAA,EACd;AACF;AAKA,SAAS,qBACP,QACA,SACA,aACgB;AAChB,QAAM,YAAY,aAAa,OAAO,IAAI;AAG1C,QAAM,wBAAwB,8BAA8B,OAAO,WAAW;AAC9E,QAAM,gCAAgC,sCAAsC,MAAM;AAElF,QAAM,UAAU,YACb,QAAQ,iCAAiC,QAAQ,kBAAkB,EACnE,QAAQ,uBAAuB,SAAS,EACxC,QAAQ,oCAAoC,qBAAqB,EACjE,QAAQ,6CAA6C,6BAA6B;AAErF,SAAO;AAAA,IACL,MAAM,GAAG,QAAQ,aAAa,YAAY,SAAS;AAAA,IACnD;AAAA,IACA,MAAM;AAAA,IACN,WAAW;AAAA;AAAA,IACX,YAAY,OAAO;AAAA,EACrB;AACF;AAKO,SAAS,eACd,SACA,SACkB;AAClB,QAAM,WAAW,eAAe,OAAO;AACvC,QAAM,SAA2B,CAAC;AAGlC,SAAO,KAAK,kBAAkB,SAAS,UAAU,eAAe,YAAY,CAAC,CAAC;AAG9E,SAAO,KAAK,kCAAkC,UAAU,eAAe,4BAA4B,CAAC,CAAC;AAGrG,SAAO,KAAK,wBAAwB,SAAS,UAAU,eAAe,kBAAkB,CAAC,CAAC;AAG1F,aAAW,UAAU,OAAO,OAAO,OAAO,GAAG;AAC3C,QAAI,OAAO,SAAS,QAAQ;AAC1B;AAAA,IACF;AAGA,WAAO,KAAK,qBAAqB,QAAQ,UAAU,eAAe,SAAS,CAAC,CAAC;AAG7E,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,aAAa;AAAA,MAC5B,eAAe,kBAAkB;AAAA,IACnC,CAAC;AAGD,WAAO,KAAK,oBAAoB,QAAQ,UAAU,eAAe,QAAQ,CAAC,CAAC;AAAA,EAC7E;AAEA,SAAO;AACT;AAKO,SAAS,aAAa,OAA+B;AAC1D,SAAO,MAAM;AACf;AAUO,SAAS,6BACd,iBACA,gBACmC;AACnC,QAAM,gBAAgB;AACtB,QAAM,eAAe,OAAO,aAAa;AAGzC,MAAI,mBAAmB,gBAAgB,SAAS,uBAAuB,GAAG;AACxE,WAAO;AAAA,MACL,MAAM,mBAAmB,eAAe,4BAA4B;AAAA,MACpE,SAAS;AAAA,MACT;AAAA,MACA,mBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,mBAAmB,cAAc;AAEnC,QAAI,iBAAiB;AAEnB,YAAM,QAAQ,gBAAgB,MAAM,IAAI;AACxC,YAAM,SAAmB,CAAC;AAC1B,UAAI,WAAW;AAEf,eAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAM,OAAO,MAAM,CAAC;AAEpB,YAAI,CAAC,YAAY,KAAK,KAAK,MAAM,MAAM;AAErC,iBAAO,KAAK,YAAY;AACxB,qBAAW;AAAA,QACb;AACA,eAAO,KAAK,IAAI;AAAA,MAClB;AAEA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS,OAAO,KAAK,IAAI;AAAA,QACzB;AAAA,QACA,mBAAmB;AAAA,MACrB;AAAA,IACF,OAAO;AAEL,aAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA;AAAA;AAAA;AAAA,EAIf,YAAY;AAAA;AAAA;AAAA,QAGN;AAAA,QACA,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,EACF,OAAO;AAEL,QAAI,iBAAiB;AAMnB,YAAM,wBAAwB;AAC9B,YAAM,QAAQ,gBAAgB,MAAM,qBAAqB;AAEzD,UAAI,OAAO;AAET,cAAM,iBAAiB,gBAAgB,QAAQ,aAAa;AAC5D,YAAI,mBAAmB,IAAI;AACzB,iBAAO;AAAA,QACT;AAGA,YAAI,QAAQ;AACZ,YAAI,aAAa;AACjB,YAAI,YAAY;AAEhB,iBAAS,IAAI,gBAAgB,IAAI,gBAAgB,QAAQ,KAAK;AAC5D,gBAAM,OAAO,gBAAgB,CAAC;AAC9B,cAAI,SAAS,KAAK;AAChB,yBAAa;AACb;AAAA,UACF,WAAW,SAAS,KAAK;AACvB;AACA,gBAAI,cAAc,UAAU,GAAG;AAC7B,0BAAY;AACZ;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,YAAI,cAAc,IAAI;AAEpB,gBAAM,cAAc,gBAAgB,UAAU,GAAG,SAAS;AAC1D,gBAAM,cAAc,YAAY,YAAY,IAAI;AAGhD,gBAAM,UACJ,gBAAgB,UAAU,GAAG,cAAc,CAAC,IAC5C,eAAe,OACf,gBAAgB,UAAU,cAAc,CAAC;AAE3C,iBAAO;AAAA,YACL,MAAM;AAAA,YACN;AAAA,YACA;AAAA,YACA,mBAAmB;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAEA,aAAO;AAAA,IACT,OAAO;AAEL,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AEt3CA,SAASE,gBAAe,SAAoD;AAC1E,SAAO;AAAA,IACL,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,aAAa,SAAS,eAAe;AAAA,IACrC,aAAa,SAAS,eAAe;AAAA,EACvC;AACF;AAKA,SAASC,kBAAyB;AAChC,SAAO;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;AA4BT;AAKA,SAAS,iBACP,cACA,UACA,QACA,SACe;AACf,QAAM,OAAO,SAAS;AAGtB,MAAI,CAAC,cAAc,cAAc,YAAY,EAAE,SAAS,YAAY,GAAG;AACrE,WAAO;AAAA,EACT;AAGA,MAAI,SAAS,eAAe;AAC1B,WAAO;AAAA,EACT;AAGA,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,mBAAmB,cAAc,QAAQ;AAAA,IAElD,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,gBAAgB,cAAc,QAAQ;AAAA,IAE/C,KAAK;AAAA,IACL,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AAAA,IACL,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,IAAI,YAAY;AAAA,IAEzB,KAAK;AACH,aAAO,iBAAiB,cAAc,QAAQ;AAAA,IAEhD,KAAK;AACH,aAAO,oBAAoB,cAAc,UAAU,OAAO;AAAA,IAE5D;AAEE,aAAO,IAAI,YAAY;AAAA,EAC3B;AACF;AAKA,SAAS,mBAAmB,cAAsB,UAAsC;AAEtF,MAAI,iBAAiB,QAAQ;AAC3B,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,iBAAiB,UAAU,iBAAiB,OAAO;AACrD,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,OAAO,GAAG;AAClC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,OAAO,GAAG;AAClC,WAAO,IAAI,YAAY;AAAA,EACzB;AAGA,MAAI,aAAa,SAAS,OAAO,KAAK,aAAa,SAAS,OAAO,KAAK,aAAa,SAAS,QAAQ,GAAG;AACvG,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,KAAK,KAAK,aAAa,SAAS,SAAS,GAAG;AACpE,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,MAAM,KAAK,aAAa,SAAS,MAAM,GAAG;AAClE,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,iBAAiB,UAAU,iBAAiB,SAAS;AACvD,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,MAAM,GAAG;AACjC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,SAAS,GAAG;AACpC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,MAAM,GAAG;AACjC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,SAAS,GAAG;AACpC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,KAAK,KAAK,aAAa,SAAS,QAAQ,GAAG;AACnE,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,OAAO,GAAG;AAClC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,OAAO,KAAK,aAAa,SAAS,QAAQ,KAAK,aAAa,SAAS,KAAK,GAAG;AACrG,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,MAAM,GAAG;AACjC,WAAO,IAAI,YAAY;AAAA,EACzB;AAGA,QAAM,SAAU,SAAiC;AACjD,MAAI,UAAU,UAAU,IAAI;AAC1B,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,SAAO,IAAI,YAAY;AACzB;AAKA,SAAS,gBAAgB,cAAsB,UAAsC;AACnF,MAAI,aAAa,SAAS,OAAO,KAAK,aAAa,SAAS,UAAU,GAAG;AACvE,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,OAAO,KAAK,aAAa,SAAS,QAAQ,KAAK,aAAa,SAAS,MAAM,GAAG;AACtG,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,OAAO,KAAK,aAAa,SAAS,MAAM,KAAK,aAAa,SAAS,UAAU,GAAG;AACxG,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,KAAK,GAAG;AAChC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,MAAI,aAAa,SAAS,MAAM,GAAG;AACjC,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,SAAO,IAAI,YAAY;AACzB;AAKA,SAAS,iBAAiB,cAAsB,UAAsC;AACpF,QAAM,aAAc,SAAgE;AACpF,MAAI,CAAC,cAAc,WAAW,WAAW,GAAG;AAC1C,WAAO,IAAI,YAAY;AAAA,EACzB;AAGA,QAAM,SAAS,WAAW,IAAI,OAAK,OAAO,MAAM,WAAW,IAAI,EAAE,KAAK;AACtE,QAAM,YAAY,OAAO,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAErD,SAAO,IAAI,YAAY,+BAA+B,SAAS;AACjE;AAKA,SAAS,oBACP,cACA,UACA,SACQ;AACR,QAAM,WAAY,SAA+B;AACjD,MAAI,CAAC,UAAU;AACb,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,QAAM,aAAa,QAAQ,QAAQ;AACnC,MAAI,CAAC,cAAc,WAAW,SAAS,UAAU,CAAC,WAAW,QAAQ;AACnE,WAAO,IAAI,YAAY;AAAA,EACzB;AAEA,QAAM,YAAY,WAAW,OAAO,IAAI,OAAK,IAAI,CAAC,GAAG,EAAE,KAAK,IAAI;AAChE,SAAO,IAAI,YAAY,+BAA+B,SAAS;AACjE;AAKA,SAAS,wBACP,cACA,UACA,QACA,SACA,gBAC0C;AAC1C,MAAI,SAAS,SAAS,eAAe;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,WAAY,SAAmC;AACrD,QAAM,SAAU,SAAiC;AAGjD,MAAI,aAAa,eAAe,CAAC,QAAQ;AACvC,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,GAAG,YAAY,YAAY,CAAC;AAC/C,QAAMC,cAAc,SAAoC,YAAY;AACpE,QAAM,eAAe,QAAQ,MAAM;AAGnC,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAGA,MAAI;AACJ,MAAIA,aAAY;AACd,WAAO,IAAI,UAAU,QAAQ,MAAM;AAAA,EACrC,OAAO;AACL,WAAO,IAAI,UAAU,QAAQ,MAAM,+CAA+C,MAAM;AAAA,EAC1F;AAGA,MAAI;AACJ,MAAI,WAAW,OAAO,MAAM;AAC1B,sBAAkB,OAAO,cAAc,KAAK,MAAM;AAAA,EACpD;AAEA,SAAO,EAAE,MAAM,QAAQ,gBAAgB;AACzC;AAKA,SAAS,gBACP,QACA,SACA,SACA,aACyB;AAEzB,MAAI,OAAO,SAAS,QAAQ;AAC1B,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,aAAa,OAAO,IAAI;AAC1C,QAAM,cAAc,GAAG,SAAS;AAEhC,QAAM,aAAuB,CAAC;AAC9B,QAAM,UAAoB,CAAC;AAG3B,MAAI,OAAO,YAAY;AACrB,eAAW,CAAC,UAAU,IAAI,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAEhE,UAAI,KAAK,SAAS,eAAe;AAC/B,cAAM,cAAc,wBAAwB,UAAU,MAAM,QAAQ,SAAS,QAAQ,cAAc;AACnG,YAAI,aAAa;AACf,qBAAW,KAAK,YAAY,IAAI;AAChC,cAAI,YAAY,QAAQ;AACtB,oBAAQ,KAAK,YAAY,MAAM;AAAA,UACjC;AAAA,QACF;AACA;AAAA,MACF;AAGA,YAAM,OAAO,iBAAiB,UAAU,MAAM,QAAQ,OAAO;AAC7D,UAAI,MAAM;AACR,mBAAW,KAAK,IAAI;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAGA,MAAI,UAAU;AACd,YAAU,QAAQ,QAAQ,4BAA4B,QAAQ,cAAc;AAC5E,YAAU,QAAQ,QAAQ,uBAAuB,SAAS;AAG1D,QAAM,gBAAgB,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC;AAC1C,QAAM,aAAa,cAAc,SAAS,IACtC,OAAO,cAAc,KAAK,IAAI,IAC9B;AACJ,YAAU,QAAQ,QAAQ,oBAAoB,UAAU;AAGxD,QAAM,gBAAgB,WAAW,SAAS,IACtC,WAAW,IAAI,OAAK,eAAe,CAAC,EAAE,EAAE,KAAK,IAAI,IACjD;AACJ,YAAU,QAAQ,QAAQ,uBAAuB,aAAa;AAE9D,SAAO;AAAA,IACL,MAAM;AAAA,IACN,YAAY,OAAO;AAAA,IACnB,MAAM,GAAG,QAAQ,WAAW,IAAI,WAAW;AAAA,IAC3C;AAAA,IACA,WAAW;AAAA;AAAA,EACb;AACF;AAKO,SAAS,kBACd,SACA,SACoB;AACpB,QAAM,WAAWF,gBAAe,OAAO;AACvC,QAAM,cAAcC,gBAAe;AACnC,QAAM,YAAgC,CAAC;AAEvC,aAAW,UAAU,OAAO,OAAO,OAAO,GAAG;AAC3C,UAAM,UAAU,gBAAgB,QAAQ,SAAS,UAAU,WAAW;AACtE,QAAI,SAAS;AACX,gBAAU,KAAK,OAAO;AAAA,IACxB;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,eAAe,SAAmC;AAChE,SAAO,QAAQ;AACjB;;;ANraA,SAAS,2BAA2B,eAAoC;AACtE,QAAM,iBAAiB,oBAAI,IAAY;AAEvC,MAAI,CAAC,WAAW,aAAa,GAAG;AAC9B,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,QAAQ,YAAY,aAAa;AAEvC,UAAM,yBAAyB;AAE/B,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,KAAK,MAAM,sBAAsB;AAC/C,UAAI,OAAO;AACT,uBAAe,IAAI,MAAM,CAAC,CAAC;AAAA,MAC7B;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAKA,IAAM,wBAA4C;AAAA,EAChD,QAAQ;AAAA,IACN;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,MAAM;AAAA,MACN,OAAO;AAAA,MACP,aAAa;AAAA,MACb,aAAa;AAAA,MACb,OAAO;AAAA,IACT;AAAA,EACF;AACF;AA2FA,SAASE,gBAAe,SAAiD;AACvE,SAAO;AAAA,IACL,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,YAAY,SAAS,cAAc;AAAA,IACnC,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,oBAAoB,SAAS,sBAAsB;AAAA,IACnD,gBAAgB,SAAS,kBAAkB;AAAA,IAC3C,eAAe,SAAS,iBAAiB;AAAA,IACzC,mBAAmB,SAAS,qBAAqB;AAAA,IACjD,aAAa,SAAS,eAAe;AAAA,IACrC,YAAY,SAAS;AAAA,IACrB,WAAW,SAAS;AAAA,EACtB;AACF;AAQe,SAAR,cAA+B,SAA8C;AAClF,QAAM,WAAWA,gBAAe,OAAO;AAGvC,QAAM,qBAAqB;AAAA,IACzB,MAAM;AAAA,IACN,aAAa;AAAA,IAEb,UAAU,OAAO,QAAsD;AACrE,YAAM,mBAAqC;AAAA,QACzC,YAAY,SAAS;AAAA,QACrB,WAAW,SAAS;AAAA,QACpB,aAAa,IAAI;AAAA,MACnB;AAEA,YAAM,UAA6B,CAAC;AACpC,YAAM,gBAAgB,KAAK,IAAI,KAAK,SAAS,cAAc;AAC3D,YAAM,iBAAiB,2BAA2B,aAAa;AAK/D,UAAI,IAAI,YAAY,QAAW;AAE7B,YAAI,IAAI,QAAQ,WAAW,GAAG;AAC5B,iBAAO;AAAA,QACT;AAGA,cAAM,mBAAmB,IAAI;AAAA,UAC3B,IAAI,QACD,OAAO,CAAC,MAAM,EAAE,eAAe,OAAO,EACtC,IAAI,CAAC,MAAM,EAAE,UAAU;AAAA,QAC5B;AAEA,YAAI,iBAAiB,OAAO,GAAG;AAC7B,gBAAM,eAAe,OAAO;AAAA,YAC1B,OAAO,QAAQ,IAAI,OAAO,EAAE,OAAO,CAAC,CAAC,IAAI,MAAM,iBAAiB,IAAI,IAAI,CAAC;AAAA,UAC3E;AAEA,gBAAM,mBAAmB,mBAAmB,cAAc,gBAAgB;AAE1E,qBAAW,aAAa,kBAAkB;AACxC,kBAAM,YAAY,UAAU,OAAO,CAAC;AAEpC,gBAAI,eAAe,IAAI,SAAS,GAAG;AACjC,kBAAI,OAAO,MAAM,uBAAuB,SAAS,mBAAmB;AACpE;AAAA,YACF;AAEA,oBAAQ,KAAK;AAAA,cACX,MAAM,iBAAiB,WAAW,SAAS,cAAc;AAAA,cACzD,SAAS,UAAU;AAAA,cACnB,MAAM;AAAA,cACN,UAAU;AAAA,gBACR;AAAA,gBACA,eAAe;AAAA,cACjB;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAGA,cAAM,eAAe,IAAI,QAAQ;AAAA,UAC/B,CAAC,MAAM,EAAE,eAAe,cAAc,EAAE,eAAe;AAAA,QACzD;AAEA,YAAI,aAAa,SAAS,GAAG;AAE3B,gBAAM,kBAAkB;AAAA,YACtB;AAAA,YACA;AAAA,UACF;AAEA,qBAAW,aAAa,iBAAiB;AACvC,oBAAQ,KAAK;AAAA,cACX,MAAM,iBAAiB,WAAW,SAAS,cAAc;AAAA,cACzD,SAAS,UAAU;AAAA,cACnB,MAAM;AAAA,cACN,UAAU;AAAA,gBACR,WAAW,UAAU,OAAO,CAAC;AAAA,gBAC7B,eAAe,UAAU;AAAA,cAC3B;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,OAAO;AAGL,cAAM,aAAa,mBAAmB,IAAI,SAAS,gBAAgB;AAEnE,mBAAW,aAAa,YAAY;AAClC,gBAAM,YAAY,UAAU,OAAO,CAAC;AACpC,cAAI,UAAU,SAAS,YAAY,eAAe,IAAI,SAAS,GAAG;AAChE,gBAAI,OAAO,MAAM,0BAA0B,SAAS,mBAAmB;AACvE;AAAA,UACF;AAEA,kBAAQ,KAAK;AAAA,YACX,MAAM,iBAAiB,WAAW,SAAS,cAAc;AAAA,YACzD,SAAS,UAAU;AAAA,YACnB,MAAM;AAAA,YACN,UAAU;AAAA,cACR;AAAA,cACA,eAAe,UAAU;AAAA,YAC3B;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,iBAAiB;AAAA,IACrB,MAAM;AAAA,IACN,aAAa;AAAA,IAEb,UAAU,OAAO,QAAsD;AACrE,YAAM,eAAsC;AAAA,QAC1C,gBAAgB,SAAS;AAAA,QACzB,oBAAoB,SAAS;AAAA,QAC7B,WAAW,SAAS;AAAA,QACpB,eAAe,SAAS;AAAA,QACxB,aAAa,IAAI;AAAA,MACnB;AAEA,YAAM,SAAS,eAAe,IAAI,SAAS,YAAY;AACvD,YAAM,UAA6B,OAAO,IAAI,CAAC,WAAW;AAAA,QACxD,MAAM,aAAa,KAAK;AAAA,QACxB,SAAS,MAAM;AAAA,QACf,MAAM;AAAA;AAAA,QAEN,cAAc,CAAC,MAAM;AAAA,QACrB,UAAU;AAAA,UACR,WAAW,MAAM;AAAA,UACjB,YAAY,MAAM;AAAA,QACpB;AAAA,MACF,EAAE;AAIF,YAAM,yBAAyB,KAAK,IAAI,KAAK,yBAAyB;AACtE,YAAM,gBAAgB,KAAK,IAAI,KAAK,gBAAgB;AAEpD,UAAI,kBAAiC;AACrC,UAAI;AAEJ,UAAI,WAAW,sBAAsB,GAAG;AAEtC,yBAAiB;AACjB,YAAI;AACF,4BAAkB,aAAa,wBAAwB,OAAO;AAAA,QAChE,QAAQ;AACN,4BAAkB;AAAA,QACpB;AAAA,MACF,WAAW,WAAW,aAAa,GAAG;AAEpC,yBAAiB;AACjB,YAAI;AACF,4BAAkB,aAAa,eAAe,OAAO;AAAA,QACvD,QAAQ;AACN,4BAAkB;AAAA,QACpB;AAAA,MACF,OAAO;AAEL,yBAAiB;AAAA,MACnB;AAEA,YAAM,eAAe,6BAA6B,iBAAiB,cAAc;AAEjF,UAAI,gBAAgB,CAAC,aAAa,mBAAmB;AACnD,gBAAQ,KAAK;AAAA,UACX,MAAM,aAAa;AAAA,UACnB,SAAS,aAAa;AAAA,UACtB,MAAM;AAAA,UACN,cAAc;AAAA;AAAA,UACd,UAAU;AAAA,YACR,kBAAkB;AAAA,YAClB,gBAAgB,aAAa;AAAA,UAC/B;AAAA,QACF,CAAC;AACD,YAAI,OAAO,KAAK,+CAA+C,aAAa,IAAI,EAAE;AAAA,MACpF,WAAW,cAAc,mBAAmB;AAC1C,YAAI,OAAO,KAAK,6CAA6C;AAAA,MAC/D;AAEA,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,mBAAmB;AAAA,IACvB,MAAM;AAAA,IACN,aAAa;AAAA,IAEb,UAAU,OAAO,QAAsD;AACrE,YAAM,iBAA0C;AAAA,QAC9C,gBAAgB,SAAS;AAAA,QACzB,aAAa,SAAS;AAAA,QACtB,aAAa,SAAS;AAAA,MACxB;AAEA,YAAM,YAAY,kBAAkB,IAAI,SAAS,cAAc;AAE/D,aAAO,UAAU,IAAI,CAAC,aAAa;AAAA,QACjC,MAAM,eAAe,OAAO;AAAA,QAC5B,SAAS,QAAQ;AAAA,QACjB,MAAM;AAAA;AAAA,QAEN,cAAc,CAAC,QAAQ;AAAA,QACvB,UAAU;AAAA,UACR,aAAa,QAAQ;AAAA,UACrB,YAAY,QAAQ;AAAA,QACtB;AAAA,MACF,EAAE;AAAA,IACJ;AAAA,EACF;AAGA,QAAM,aAAa,CAAC,kBAAkB;AACtC,MAAI,SAAS,gBAAgB;AAC3B,eAAW,KAAK,cAAc;AAAA,EAChC;AACA,MAAI,SAAS,mBAAmB;AAC9B,eAAW,KAAK,gBAAgB;AAAA,EAClC;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd;AAAA,EACF;AACF;","names":["columnMethod","TYPE_METHOD_MAP","generateTimestamp","typeDef","isCompoundType","resolveOptions","getStubContent","isNullable","resolveOptions"]}
package/dist/index.cjs CHANGED
@@ -1401,8 +1401,12 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1401
1401
  const properties = schema.properties ?? {};
1402
1402
  for (const [propName, propDef] of Object.entries(properties)) {
1403
1403
  const snakeName = toSnakeCase(propName);
1404
- const phpType = getPhpDocType(propDef, schemas);
1405
- docProperties.push(` * @property ${phpType} $${snakeName}`);
1404
+ const typeDef = options.customTypes.get(propDef.type);
1405
+ const isCompoundType = typeDef?.compound && typeDef.expand;
1406
+ if (!isCompoundType) {
1407
+ const phpType = getPhpDocType(propDef, schemas);
1408
+ docProperties.push(` * @property ${phpType} $${snakeName}`);
1409
+ }
1406
1410
  if (propDef.type === "Association") {
1407
1411
  const assoc = propDef;
1408
1412
  if (assoc.target) {
@@ -1433,14 +1437,19 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1433
1437
  const propWithOptions = propDef;
1434
1438
  const isFillable = propWithOptions.fillable !== false;
1435
1439
  const isHidden = propWithOptions.hidden === true;
1436
- const typeDef = options.customTypes.get(propDef.type);
1437
- const isCompoundType = typeDef?.compound && typeDef.expand;
1438
- if (isCompoundType && typeDef.expand) {
1440
+ const typeDef2 = options.customTypes.get(propDef.type);
1441
+ const isCompoundType2 = typeDef2?.compound && typeDef2.expand;
1442
+ if (isCompoundType2 && typeDef2.expand) {
1439
1443
  const fieldOverrides = propWithOptions.fields ?? {};
1440
- for (const field of typeDef.expand) {
1444
+ const basePropWithNullable = propDef;
1445
+ for (const field of typeDef2.expand) {
1441
1446
  const suffixSnake = toSnakeCase(field.suffix);
1442
1447
  const fieldName = `${snakeName}_${suffixSnake}`;
1443
1448
  const override = fieldOverrides[field.suffix];
1449
+ const fieldNullable = override?.nullable ?? basePropWithNullable.nullable ?? false;
1450
+ const phpType = field.typescript?.type === "number" ? "int" : "string";
1451
+ const nullSuffix = fieldNullable ? "|null" : "";
1452
+ docProperties.push(` * @property ${phpType}${nullSuffix} $${fieldName}`);
1444
1453
  const fieldFillable = override?.fillable !== void 0 ? override.fillable : isFillable;
1445
1454
  if (fieldFillable) {
1446
1455
  fillable.push(` '${fieldName}',`);
@@ -1462,8 +1471,8 @@ function generateEntityBaseModel(schema, schemas, options, stubContent, authStub
1462
1471
  hidden.push(` '${snakeName}',`);
1463
1472
  }
1464
1473
  }
1465
- if (typeDef?.compound && typeDef.accessors) {
1466
- for (const accessor of typeDef.accessors) {
1474
+ if (typeDef2?.compound && typeDef2.accessors) {
1475
+ for (const accessor of typeDef2.accessors) {
1467
1476
  const accessorName = `${snakeName}_${toSnakeCase(accessor.name)}`;
1468
1477
  appends.push(` '${accessorName}',`);
1469
1478
  const methodName = toPascalCase(accessorName);