@famgia/omnify-types 0.0.77 → 0.0.78

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/i18n.ts"],"sourcesContent":["/**\n * @famgia/omnify-types\n * Core type definitions for omnify-schema\n *\n * This package provides shared TypeScript interfaces and types\n * used across all omnify packages.\n */\n\n// ============================================================================\n// Schema Types\n// ============================================================================\n\nexport type {\n // Property types\n BuiltInPropertyType,\n PropertyType,\n // Association types\n AssociationRelation,\n ReferentialAction,\n PivotFieldDefinition,\n AssociationDefinition,\n // Validation rules\n ValidationRules,\n // Property definitions\n BasePropertyDefinition,\n CompoundFieldOverride,\n StringPropertyDefinition,\n NumericPropertyDefinition,\n EnumPropertyDefinition,\n EnumRefPropertyDefinition,\n FilePropertyDefinition,\n InlineEnumValue,\n PropertyDefinition,\n // Schema options\n IdType,\n IndexType,\n IndexDefinition,\n SchemaOptions,\n // Schema definitions\n SchemaKind,\n SchemaDefinition,\n LoadedSchema,\n SchemaCollection,\n} from './schema.js';\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\nexport type {\n // Database config\n DatabaseDriver,\n DatabaseConfig,\n // Output config\n LaravelOutputConfig,\n TypeScriptOutputConfig,\n OutputConfig,\n // Main config\n OmnifyConfig,\n ResolvedOmnifyConfig,\n} from './config.js';\n\n// ============================================================================\n// Plugin Types\n// ============================================================================\n\nexport type {\n // Type definitions\n SqlColumnDefinition,\n TypeScriptTypeInfo,\n ExpandedFieldDefinition,\n CompoundTypeAccessor,\n CustomTypeDefinition,\n // Plugin interface\n PluginContext,\n PluginLogger,\n OmnifyPlugin,\n PluginFactory,\n PluginEnumDefinition,\n PluginEnumValue,\n // Generator types\n GeneratorOutputType,\n GeneratorOutput,\n GeneratorContext,\n GeneratorDefinition,\n // Schema change types\n PropertySnapshot,\n IndexSnapshot,\n SchemaChangeType,\n ColumnChange,\n IndexChange,\n SchemaChange,\n // Plugin config schema (WordPress-like)\n PluginConfigFieldType,\n PluginConfigSelectOption,\n PluginConfigField,\n PluginConfigSchema,\n} from './plugin.js';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport type {\n ErrorCode,\n ErrorLocation,\n OmnifyErrorInfo,\n Result,\n} from './errors.js';\n\nexport { ok, err } from './errors.js';\n\n// ============================================================================\n// Internationalization (i18n) Types\n// ============================================================================\n\nexport type {\n LocaleCode,\n LocaleMap,\n LocalizedString,\n LocaleConfig,\n LocaleResolutionOptions,\n} from './i18n.js';\n\nexport {\n DEFAULT_LOCALE_CONFIG,\n isLocaleMap,\n isLocalizedString,\n resolveLocalizedString,\n getAvailableLocales,\n toLocaleMap,\n mergeLocalizedStrings,\n} from './i18n.js';\n","/**\n * @famgia/omnify-types - Error Types\n *\n * Type definitions for omnify error handling.\n * Errors follow the format: file:line + message + suggestion\n */\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/**\n * Error code categories:\n * - E0xx: Configuration errors\n * - E1xx: Schema parsing errors\n * - E2xx: Schema validation errors\n * - E3xx: Plugin errors\n * - E4xx: Atlas/Database errors\n * - E5xx: Generation errors\n * - E9xx: Internal errors\n */\nexport type ErrorCode =\n // Configuration errors (E0xx)\n | 'E001' // Config file not found\n | 'E002' // Invalid config format\n | 'E003' // Missing required config field\n | 'E004' // Invalid database driver\n | 'E005' // Invalid output path\n // Schema parsing errors (E1xx)\n | 'E101' // Schema file not found\n | 'E102' // Invalid YAML syntax\n | 'E103' // Invalid JSON syntax\n | 'E104' // Empty schema file\n | 'E105' // Schema read error\n // Schema validation errors (E2xx)\n | 'E201' // Invalid property type\n | 'E202' // Missing required field\n | 'E203' // Invalid association target\n | 'E204' // Circular reference detected\n | 'E205' // Duplicate schema name\n | 'E206' // Invalid schema options\n | 'E207' // Invalid enum values\n | 'E208' // Orphaned inverse relation\n // Plugin errors (E3xx)\n | 'E301' // Plugin not found\n | 'E302' // Plugin load error\n | 'E303' // Invalid plugin format\n | 'E304' // Plugin type conflict\n | 'E305' // Plugin setup error\n // Atlas/Database errors (E4xx)\n | 'E401' // Atlas CLI not found\n | 'E402' // Atlas diff failed\n | 'E403' // Database connection error\n | 'E404' // HCL generation error\n | 'E405' // Lock file error\n // Generation errors (E5xx)\n | 'E501' // Migration generation failed\n | 'E502' // TypeScript generation failed\n | 'E503' // Output write error\n | 'E504' // Template error\n // Internal errors (E9xx)\n | 'E901' // Unexpected error\n | 'E902'; // Not implemented\n\n// ============================================================================\n// Error Info\n// ============================================================================\n\n/**\n * Source location where an error occurred.\n */\nexport interface ErrorLocation {\n /** File path where the error occurred */\n readonly file?: string;\n /** Line number (1-based) */\n readonly line?: number;\n /** Column number (1-based) */\n readonly column?: number;\n}\n\n/**\n * Structured error information for omnify errors.\n */\nexport interface OmnifyErrorInfo {\n /** Error code (e.g., 'E201') */\n readonly code: ErrorCode;\n /** Human-readable error message */\n readonly message: string;\n /** Location where the error occurred */\n readonly location?: ErrorLocation;\n /** Suggested fix for the error */\n readonly suggestion?: string;\n /** Additional context or details */\n readonly details?: Record<string, unknown>;\n /** Original error if this wraps another error */\n readonly cause?: Error;\n}\n\n/**\n * Result type for operations that may fail.\n */\nexport type Result<T, E = OmnifyErrorInfo> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: E };\n\n/**\n * Helper to create a successful result.\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Helper to create an error result.\n */\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n","/**\n * @famgia/omnify-types - Internationalization (i18n) Types\n *\n * Types for multi-language support in schema definitions.\n * Supports displayName, description, and other localizable strings.\n */\n\n// ============================================================================\n// Locale Types\n// ============================================================================\n\n/**\n * Supported locale code format (ISO 639-1 language codes).\n * Examples: 'en', 'ja', 'vi', 'zh', 'ko', 'fr', 'de', 'es', 'pt'\n */\nexport type LocaleCode = string;\n\n/**\n * Map of locale codes to localized strings.\n * @example { en: 'User Name', ja: 'ユーザー名', vi: 'Tên người dùng' }\n */\nexport type LocaleMap = Readonly<Record<LocaleCode, string>>;\n\n/**\n * Localized string - can be either a simple string (default locale)\n * or a map of locale codes to localized strings.\n *\n * @example\n * // Simple string (uses default locale)\n * displayName: \"User Name\"\n *\n * // Multi-language object\n * displayName:\n * en: \"User Name\"\n * ja: \"ユーザー名\"\n * vi: \"Tên người dùng\"\n */\nexport type LocalizedString = string | LocaleMap;\n\n// ============================================================================\n// Locale Configuration\n// ============================================================================\n\n/**\n * Locale configuration for omnify projects.\n * Configured in .omnify.yaml or omnify.config.ts\n */\nexport interface LocaleConfig {\n /**\n * List of supported locale codes.\n * @default ['en']\n * @example ['en', 'ja', 'vi']\n */\n readonly locales: readonly LocaleCode[];\n\n /**\n * Default locale code used when:\n * - displayName is a simple string\n * - Requested locale is not found and no fallback matches\n * @default 'en'\n */\n readonly defaultLocale: LocaleCode;\n\n /**\n * Fallback locale code used when requested locale is not found.\n * If not set, falls back to defaultLocale.\n * @example 'en' - If 'ko' not found, try 'en' before defaultLocale\n */\n readonly fallbackLocale?: LocaleCode;\n}\n\n/**\n * Default locale configuration.\n */\nexport const DEFAULT_LOCALE_CONFIG: LocaleConfig = {\n locales: ['en'],\n defaultLocale: 'en',\n};\n\n// ============================================================================\n// Resolution Options\n// ============================================================================\n\n/**\n * Options for resolving localized strings.\n */\nexport interface LocaleResolutionOptions {\n /**\n * Locale to resolve to.\n * If not provided, uses defaultLocale from config.\n */\n readonly locale?: LocaleCode;\n\n /**\n * Locale configuration.\n * If not provided, uses DEFAULT_LOCALE_CONFIG.\n */\n readonly config?: LocaleConfig;\n\n /**\n * Whether to throw an error if locale is not found.\n * If false, returns undefined for missing locales.\n * @default false\n */\n readonly strict?: boolean;\n}\n\n// ============================================================================\n// Utility Type Guards\n// ============================================================================\n\n/**\n * Check if a value is a LocaleMap (object with locale keys).\n */\nexport function isLocaleMap(value: unknown): value is LocaleMap {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n // Check that all values are strings\n return Object.values(value).every(v => typeof v === 'string');\n}\n\n/**\n * Check if a value is a LocalizedString.\n */\nexport function isLocalizedString(value: unknown): value is LocalizedString {\n return typeof value === 'string' || isLocaleMap(value);\n}\n\n// ============================================================================\n// Resolution Functions\n// ============================================================================\n\n/**\n * Resolve a localized string to a specific locale.\n *\n * Resolution order:\n * 1. Requested locale (if provided)\n * 2. Fallback locale (if configured)\n * 3. Default locale\n * 4. First available locale (if none of the above match)\n * 5. undefined (if strict=false) or throws error (if strict=true)\n *\n * @param value - The localized string value\n * @param options - Resolution options\n * @returns The resolved string or undefined\n *\n * @example\n * // Simple string\n * resolveLocalizedString('Hello') // => 'Hello'\n *\n * // Multi-language with locale\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ja' })\n * // => 'こんにちは'\n *\n * // Multi-language with fallback\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ko' })\n * // => 'Hello' (falls back to defaultLocale)\n */\nexport function resolveLocalizedString(\n value: LocalizedString | undefined | null,\n options: LocaleResolutionOptions = {}\n): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n // Simple string - return as-is\n if (typeof value === 'string') {\n return value;\n }\n\n const config = options.config ?? DEFAULT_LOCALE_CONFIG;\n const requestedLocale = options.locale ?? config.defaultLocale;\n\n // Try requested locale\n if (requestedLocale in value) {\n return value[requestedLocale];\n }\n\n // Try fallback locale\n if (config.fallbackLocale && config.fallbackLocale in value) {\n return value[config.fallbackLocale];\n }\n\n // Try default locale\n if (config.defaultLocale in value) {\n return value[config.defaultLocale];\n }\n\n // Try first available locale\n const availableLocales = Object.keys(value);\n const firstLocale = availableLocales[0];\n if (firstLocale !== undefined) {\n return value[firstLocale];\n }\n\n // No locale found\n if (options.strict) {\n throw new Error(\n `Locale '${requestedLocale}' not found in localized string. ` +\n `Available locales: none`\n );\n }\n\n return undefined;\n}\n\n/**\n * Get all available locales from a localized string.\n *\n * @param value - The localized string value\n * @returns Array of available locale codes, or ['default'] for simple strings\n */\nexport function getAvailableLocales(\n value: LocalizedString | undefined | null\n): readonly LocaleCode[] {\n if (value === undefined || value === null) {\n return [];\n }\n\n if (typeof value === 'string') {\n return ['default'];\n }\n\n return Object.keys(value);\n}\n\n/**\n * Convert a localized string to a full LocaleMap.\n * Simple strings are converted to { [defaultLocale]: value }.\n *\n * @param value - The localized string value\n * @param defaultLocale - Default locale for simple strings\n * @returns LocaleMap or undefined\n */\nexport function toLocaleMap(\n value: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n if (typeof value === 'string') {\n return { [defaultLocale]: value };\n }\n\n return value;\n}\n\n/**\n * Merge two localized strings, with the second taking precedence.\n *\n * @param base - Base localized string\n * @param override - Override localized string\n * @param defaultLocale - Default locale for simple strings\n * @returns Merged LocaleMap\n */\nexport function mergeLocalizedStrings(\n base: LocalizedString | undefined | null,\n override: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n const baseMap = toLocaleMap(base, defaultLocale);\n const overrideMap = toLocaleMap(override, defaultLocale);\n\n if (!baseMap && !overrideMap) {\n return undefined;\n }\n\n return {\n ...baseMap,\n ...overrideMap,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4GO,SAAS,GAAM,OAA4B;AAChD,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAKO,SAAS,IAAO,OAA4B;AACjD,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;;;AC3CO,IAAM,wBAAsC;AAAA,EACjD,SAAS,CAAC,IAAI;AAAA,EACd,eAAe;AACjB;AAqCO,SAAS,YAAY,OAAoC;AAC9D,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,OAAO,KAAK,EAAE,MAAM,OAAK,OAAO,MAAM,QAAQ;AAC9D;AAKO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,OAAO,UAAU,YAAY,YAAY,KAAK;AACvD;AAgCO,SAAS,uBACd,OACA,UAAmC,CAAC,GAChB;AACpB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,kBAAkB,QAAQ,UAAU,OAAO;AAGjD,MAAI,mBAAmB,OAAO;AAC5B,WAAO,MAAM,eAAe;AAAA,EAC9B;AAGA,MAAI,OAAO,kBAAkB,OAAO,kBAAkB,OAAO;AAC3D,WAAO,MAAM,OAAO,cAAc;AAAA,EACpC;AAGA,MAAI,OAAO,iBAAiB,OAAO;AACjC,WAAO,MAAM,OAAO,aAAa;AAAA,EACnC;AAGA,QAAM,mBAAmB,OAAO,KAAK,KAAK;AAC1C,QAAM,cAAc,iBAAiB,CAAC;AACtC,MAAI,gBAAgB,QAAW;AAC7B,WAAO,MAAM,WAAW;AAAA,EAC1B;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR,WAAW,eAAe;AAAA,IAE5B;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,oBACd,OACuB;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,CAAC,SAAS;AAAA,EACnB;AAEA,SAAO,OAAO,KAAK,KAAK;AAC1B;AAUO,SAAS,YACd,OACA,gBAA4B,MACL;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,CAAC,aAAa,GAAG,MAAM;AAAA,EAClC;AAEA,SAAO;AACT;AAUO,SAAS,sBACd,MACA,UACA,gBAA4B,MACL;AACvB,QAAM,UAAU,YAAY,MAAM,aAAa;AAC/C,QAAM,cAAc,YAAY,UAAU,aAAa;AAEvD,MAAI,CAAC,WAAW,CAAC,aAAa;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/i18n.ts"],"sourcesContent":["/**\n * @famgia/omnify-types\n * Core type definitions for omnify-schema\n *\n * This package provides shared TypeScript interfaces and types\n * used across all omnify packages.\n */\n\n// ============================================================================\n// Schema Types\n// ============================================================================\n\nexport type {\n // Property types\n BuiltInPropertyType,\n PropertyType,\n // Association types\n AssociationRelation,\n ReferentialAction,\n PivotFieldDefinition,\n AssociationDefinition,\n // Validation rules\n ValidationRules,\n // Property definitions\n BasePropertyDefinition,\n CompoundFieldOverride,\n StringPropertyDefinition,\n NumericPropertyDefinition,\n EnumPropertyDefinition,\n EnumRefPropertyDefinition,\n FilePropertyDefinition,\n InlineEnumValue,\n PropertyDefinition,\n // Schema options\n IdType,\n IndexType,\n IndexDefinition,\n SchemaOptions,\n // Schema definitions\n SchemaKind,\n SchemaDefinition,\n LoadedSchema,\n SchemaCollection,\n} from './schema.js';\n\n// ============================================================================\n// Configuration Types\n// ============================================================================\n\nexport type {\n // Database config\n DatabaseDriver,\n DatabaseConfig,\n // Output config\n LaravelOutputConfig,\n TypeScriptOutputConfig,\n OutputConfig,\n // Main config\n OmnifyConfig,\n ResolvedOmnifyConfig,\n} from './config.js';\n\n// ============================================================================\n// Plugin Types\n// ============================================================================\n\nexport type {\n // Type definitions\n SqlColumnDefinition,\n TypeScriptTypeInfo,\n FieldValidationRules,\n ExpandedFieldDefinition,\n CompoundTypeAccessor,\n CustomTypeDefinition,\n // Plugin interface\n PluginContext,\n PluginLogger,\n OmnifyPlugin,\n PluginFactory,\n PluginEnumDefinition,\n PluginEnumValue,\n // Generator types\n GeneratorOutputType,\n GeneratorOutput,\n GeneratorContext,\n GeneratorDefinition,\n // Schema change types\n PropertySnapshot,\n IndexSnapshot,\n SchemaChangeType,\n ColumnChange,\n IndexChange,\n SchemaChange,\n // Plugin config schema (WordPress-like)\n PluginConfigFieldType,\n PluginConfigSelectOption,\n PluginConfigField,\n PluginConfigSchema,\n} from './plugin.js';\n\n// ============================================================================\n// Error Types\n// ============================================================================\n\nexport type {\n ErrorCode,\n ErrorLocation,\n OmnifyErrorInfo,\n Result,\n} from './errors.js';\n\nexport { ok, err } from './errors.js';\n\n// ============================================================================\n// Internationalization (i18n) Types\n// ============================================================================\n\nexport type {\n LocaleCode,\n LocaleMap,\n LocalizedString,\n ValidationMessages,\n LocaleConfig,\n LocaleResolutionOptions,\n} from './i18n.js';\n\nexport {\n DEFAULT_LOCALE_CONFIG,\n isLocaleMap,\n isLocalizedString,\n resolveLocalizedString,\n getAvailableLocales,\n toLocaleMap,\n mergeLocalizedStrings,\n} from './i18n.js';\n","/**\n * @famgia/omnify-types - Error Types\n *\n * Type definitions for omnify error handling.\n * Errors follow the format: file:line + message + suggestion\n */\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/**\n * Error code categories:\n * - E0xx: Configuration errors\n * - E1xx: Schema parsing errors\n * - E2xx: Schema validation errors\n * - E3xx: Plugin errors\n * - E4xx: Atlas/Database errors\n * - E5xx: Generation errors\n * - E9xx: Internal errors\n */\nexport type ErrorCode =\n // Configuration errors (E0xx)\n | 'E001' // Config file not found\n | 'E002' // Invalid config format\n | 'E003' // Missing required config field\n | 'E004' // Invalid database driver\n | 'E005' // Invalid output path\n // Schema parsing errors (E1xx)\n | 'E101' // Schema file not found\n | 'E102' // Invalid YAML syntax\n | 'E103' // Invalid JSON syntax\n | 'E104' // Empty schema file\n | 'E105' // Schema read error\n // Schema validation errors (E2xx)\n | 'E201' // Invalid property type\n | 'E202' // Missing required field\n | 'E203' // Invalid association target\n | 'E204' // Circular reference detected\n | 'E205' // Duplicate schema name\n | 'E206' // Invalid schema options\n | 'E207' // Invalid enum values\n | 'E208' // Orphaned inverse relation\n // Plugin errors (E3xx)\n | 'E301' // Plugin not found\n | 'E302' // Plugin load error\n | 'E303' // Invalid plugin format\n | 'E304' // Plugin type conflict\n | 'E305' // Plugin setup error\n // Atlas/Database errors (E4xx)\n | 'E401' // Atlas CLI not found\n | 'E402' // Atlas diff failed\n | 'E403' // Database connection error\n | 'E404' // HCL generation error\n | 'E405' // Lock file error\n // Generation errors (E5xx)\n | 'E501' // Migration generation failed\n | 'E502' // TypeScript generation failed\n | 'E503' // Output write error\n | 'E504' // Template error\n // Internal errors (E9xx)\n | 'E901' // Unexpected error\n | 'E902'; // Not implemented\n\n// ============================================================================\n// Error Info\n// ============================================================================\n\n/**\n * Source location where an error occurred.\n */\nexport interface ErrorLocation {\n /** File path where the error occurred */\n readonly file?: string;\n /** Line number (1-based) */\n readonly line?: number;\n /** Column number (1-based) */\n readonly column?: number;\n}\n\n/**\n * Structured error information for omnify errors.\n */\nexport interface OmnifyErrorInfo {\n /** Error code (e.g., 'E201') */\n readonly code: ErrorCode;\n /** Human-readable error message */\n readonly message: string;\n /** Location where the error occurred */\n readonly location?: ErrorLocation;\n /** Suggested fix for the error */\n readonly suggestion?: string;\n /** Additional context or details */\n readonly details?: Record<string, unknown>;\n /** Original error if this wraps another error */\n readonly cause?: Error;\n}\n\n/**\n * Result type for operations that may fail.\n */\nexport type Result<T, E = OmnifyErrorInfo> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: E };\n\n/**\n * Helper to create a successful result.\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Helper to create an error result.\n */\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n","/**\n * @famgia/omnify-types - Internationalization (i18n) Types\n *\n * Types for multi-language support in schema definitions.\n * Supports displayName, description, and other localizable strings.\n */\n\n// ============================================================================\n// Locale Types\n// ============================================================================\n\n/**\n * Supported locale code format (ISO 639-1 language codes).\n * Examples: 'en', 'ja', 'vi', 'zh', 'ko', 'fr', 'de', 'es', 'pt'\n */\nexport type LocaleCode = string;\n\n/**\n * Map of locale codes to localized strings.\n * @example { en: 'User Name', ja: 'ユーザー名', vi: 'Tên người dùng' }\n */\nexport type LocaleMap = Readonly<Record<LocaleCode, string>>;\n\n/**\n * Localized string - can be either a simple string (default locale)\n * or a map of locale codes to localized strings.\n *\n * @example\n * // Simple string (uses default locale)\n * displayName: \"User Name\"\n *\n * // Multi-language object\n * displayName:\n * en: \"User Name\"\n * ja: \"ユーザー名\"\n * vi: \"Tên người dùng\"\n */\nexport type LocalizedString = string | LocaleMap;\n\n// ============================================================================\n// Locale Configuration\n// ============================================================================\n\n/**\n * Validation message templates.\n * Use placeholders: ${displayName}, ${min}, ${max}, ${length}, ${pattern}\n */\nexport interface ValidationMessages {\n readonly required?: LocaleMap;\n readonly minLength?: LocaleMap;\n readonly maxLength?: LocaleMap;\n readonly min?: LocaleMap;\n readonly max?: LocaleMap;\n readonly email?: LocaleMap;\n readonly url?: LocaleMap;\n readonly pattern?: LocaleMap;\n readonly [key: string]: LocaleMap | undefined;\n}\n\n/**\n * Locale configuration for omnify projects.\n * Configured in .omnify.yaml or omnify.config.ts\n */\nexport interface LocaleConfig {\n /**\n * List of supported locale codes.\n * @default ['en']\n * @example ['en', 'ja', 'vi']\n */\n readonly locales: readonly LocaleCode[];\n\n /**\n * Default locale code used when:\n * - displayName is a simple string\n * - Requested locale is not found and no fallback matches\n * @default 'en'\n */\n readonly defaultLocale: LocaleCode;\n\n /**\n * Fallback locale code used when requested locale is not found.\n * If not set, falls back to defaultLocale.\n * @example 'en' - If 'ko' not found, try 'en' before defaultLocale\n */\n readonly fallbackLocale?: LocaleCode;\n\n /**\n * Custom validation messages.\n * Override default messages or add new languages.\n *\n * @example\n * messages: {\n * required: {\n * ja: '${displayName}は必須です',\n * en: '${displayName} is required',\n * ko: '${displayName}은(는) 필수입니다',\n * },\n * maxLength: {\n * ja: '${displayName}は${max}文字以内で入力してください',\n * en: '${displayName} must be at most ${max} characters',\n * },\n * }\n */\n readonly messages?: ValidationMessages;\n}\n\n/**\n * Default locale configuration.\n */\nexport const DEFAULT_LOCALE_CONFIG: LocaleConfig = {\n locales: ['en'],\n defaultLocale: 'en',\n};\n\n// ============================================================================\n// Resolution Options\n// ============================================================================\n\n/**\n * Options for resolving localized strings.\n */\nexport interface LocaleResolutionOptions {\n /**\n * Locale to resolve to.\n * If not provided, uses defaultLocale from config.\n */\n readonly locale?: LocaleCode;\n\n /**\n * Locale configuration.\n * If not provided, uses DEFAULT_LOCALE_CONFIG.\n */\n readonly config?: LocaleConfig;\n\n /**\n * Whether to throw an error if locale is not found.\n * If false, returns undefined for missing locales.\n * @default false\n */\n readonly strict?: boolean;\n}\n\n// ============================================================================\n// Utility Type Guards\n// ============================================================================\n\n/**\n * Check if a value is a LocaleMap (object with locale keys).\n */\nexport function isLocaleMap(value: unknown): value is LocaleMap {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n // Check that all values are strings\n return Object.values(value).every(v => typeof v === 'string');\n}\n\n/**\n * Check if a value is a LocalizedString.\n */\nexport function isLocalizedString(value: unknown): value is LocalizedString {\n return typeof value === 'string' || isLocaleMap(value);\n}\n\n// ============================================================================\n// Resolution Functions\n// ============================================================================\n\n/**\n * Resolve a localized string to a specific locale.\n *\n * Resolution order:\n * 1. Requested locale (if provided)\n * 2. Fallback locale (if configured)\n * 3. Default locale\n * 4. First available locale (if none of the above match)\n * 5. undefined (if strict=false) or throws error (if strict=true)\n *\n * @param value - The localized string value\n * @param options - Resolution options\n * @returns The resolved string or undefined\n *\n * @example\n * // Simple string\n * resolveLocalizedString('Hello') // => 'Hello'\n *\n * // Multi-language with locale\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ja' })\n * // => 'こんにちは'\n *\n * // Multi-language with fallback\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ko' })\n * // => 'Hello' (falls back to defaultLocale)\n */\nexport function resolveLocalizedString(\n value: LocalizedString | undefined | null,\n options: LocaleResolutionOptions = {}\n): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n // Simple string - return as-is\n if (typeof value === 'string') {\n return value;\n }\n\n const config = options.config ?? DEFAULT_LOCALE_CONFIG;\n const requestedLocale = options.locale ?? config.defaultLocale;\n\n // Try requested locale\n if (requestedLocale in value) {\n return value[requestedLocale];\n }\n\n // Try fallback locale\n if (config.fallbackLocale && config.fallbackLocale in value) {\n return value[config.fallbackLocale];\n }\n\n // Try default locale\n if (config.defaultLocale in value) {\n return value[config.defaultLocale];\n }\n\n // Try first available locale\n const availableLocales = Object.keys(value);\n const firstLocale = availableLocales[0];\n if (firstLocale !== undefined) {\n return value[firstLocale];\n }\n\n // No locale found\n if (options.strict) {\n throw new Error(\n `Locale '${requestedLocale}' not found in localized string. ` +\n `Available locales: none`\n );\n }\n\n return undefined;\n}\n\n/**\n * Get all available locales from a localized string.\n *\n * @param value - The localized string value\n * @returns Array of available locale codes, or ['default'] for simple strings\n */\nexport function getAvailableLocales(\n value: LocalizedString | undefined | null\n): readonly LocaleCode[] {\n if (value === undefined || value === null) {\n return [];\n }\n\n if (typeof value === 'string') {\n return ['default'];\n }\n\n return Object.keys(value);\n}\n\n/**\n * Convert a localized string to a full LocaleMap.\n * Simple strings are converted to { [defaultLocale]: value }.\n *\n * @param value - The localized string value\n * @param defaultLocale - Default locale for simple strings\n * @returns LocaleMap or undefined\n */\nexport function toLocaleMap(\n value: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n if (typeof value === 'string') {\n return { [defaultLocale]: value };\n }\n\n return value;\n}\n\n/**\n * Merge two localized strings, with the second taking precedence.\n *\n * @param base - Base localized string\n * @param override - Override localized string\n * @param defaultLocale - Default locale for simple strings\n * @returns Merged LocaleMap\n */\nexport function mergeLocalizedStrings(\n base: LocalizedString | undefined | null,\n override: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n const baseMap = toLocaleMap(base, defaultLocale);\n const overrideMap = toLocaleMap(override, defaultLocale);\n\n if (!baseMap && !overrideMap) {\n return undefined;\n }\n\n return {\n ...baseMap,\n ...overrideMap,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4GO,SAAS,GAAM,OAA4B;AAChD,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAKO,SAAS,IAAO,OAA4B;AACjD,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;;;ACRO,IAAM,wBAAsC;AAAA,EACjD,SAAS,CAAC,IAAI;AAAA,EACd,eAAe;AACjB;AAqCO,SAAS,YAAY,OAAoC;AAC9D,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,OAAO,KAAK,EAAE,MAAM,OAAK,OAAO,MAAM,QAAQ;AAC9D;AAKO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,OAAO,UAAU,YAAY,YAAY,KAAK;AACvD;AAgCO,SAAS,uBACd,OACA,UAAmC,CAAC,GAChB;AACpB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,kBAAkB,QAAQ,UAAU,OAAO;AAGjD,MAAI,mBAAmB,OAAO;AAC5B,WAAO,MAAM,eAAe;AAAA,EAC9B;AAGA,MAAI,OAAO,kBAAkB,OAAO,kBAAkB,OAAO;AAC3D,WAAO,MAAM,OAAO,cAAc;AAAA,EACpC;AAGA,MAAI,OAAO,iBAAiB,OAAO;AACjC,WAAO,MAAM,OAAO,aAAa;AAAA,EACnC;AAGA,QAAM,mBAAmB,OAAO,KAAK,KAAK;AAC1C,QAAM,cAAc,iBAAiB,CAAC;AACtC,MAAI,gBAAgB,QAAW;AAC7B,WAAO,MAAM,WAAW;AAAA,EAC1B;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR,WAAW,eAAe;AAAA,IAE5B;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,oBACd,OACuB;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,CAAC,SAAS;AAAA,EACnB;AAEA,SAAO,OAAO,KAAK,KAAK;AAC1B;AAUO,SAAS,YACd,OACA,gBAA4B,MACL;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,CAAC,aAAa,GAAG,MAAM;AAAA,EAClC;AAEA,SAAO;AACT;AAUO,SAAS,sBACd,MACA,UACA,gBAA4B,MACL;AACvB,QAAM,UAAU,YAAY,MAAM,aAAa;AAC/C,QAAM,cAAc,YAAY,UAAU,aAAa;AAEvD,MAAI,CAAC,WAAW,CAAC,aAAa;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;","names":[]}
package/dist/index.d.cts CHANGED
@@ -29,6 +29,21 @@ type LocaleMap = Readonly<Record<LocaleCode, string>>;
29
29
  * vi: "Tên người dùng"
30
30
  */
31
31
  type LocalizedString = string | LocaleMap;
32
+ /**
33
+ * Validation message templates.
34
+ * Use placeholders: ${displayName}, ${min}, ${max}, ${length}, ${pattern}
35
+ */
36
+ interface ValidationMessages {
37
+ readonly required?: LocaleMap;
38
+ readonly minLength?: LocaleMap;
39
+ readonly maxLength?: LocaleMap;
40
+ readonly min?: LocaleMap;
41
+ readonly max?: LocaleMap;
42
+ readonly email?: LocaleMap;
43
+ readonly url?: LocaleMap;
44
+ readonly pattern?: LocaleMap;
45
+ readonly [key: string]: LocaleMap | undefined;
46
+ }
32
47
  /**
33
48
  * Locale configuration for omnify projects.
34
49
  * Configured in .omnify.yaml or omnify.config.ts
@@ -53,6 +68,24 @@ interface LocaleConfig {
53
68
  * @example 'en' - If 'ko' not found, try 'en' before defaultLocale
54
69
  */
55
70
  readonly fallbackLocale?: LocaleCode;
71
+ /**
72
+ * Custom validation messages.
73
+ * Override default messages or add new languages.
74
+ *
75
+ * @example
76
+ * messages: {
77
+ * required: {
78
+ * ja: '${displayName}は必須です',
79
+ * en: '${displayName} is required',
80
+ * ko: '${displayName}은(는) 필수입니다',
81
+ * },
82
+ * maxLength: {
83
+ * ja: '${displayName}は${max}文字以内で入力してください',
84
+ * en: '${displayName} must be at most ${max} characters',
85
+ * },
86
+ * }
87
+ */
88
+ readonly messages?: ValidationMessages;
56
89
  }
57
90
  /**
58
91
  * Default locale configuration.
@@ -284,6 +317,20 @@ interface BasePropertyDefinition {
284
317
  * @default true (all non-association fields are fillable by default)
285
318
  */
286
319
  readonly fillable?: boolean;
320
+ /**
321
+ * Placeholder text for form inputs (supports multi-language).
322
+ * Used in frontend forms to show hint text before user input.
323
+ *
324
+ * @example
325
+ * placeholder: "Enter your email"
326
+ *
327
+ * @example
328
+ * placeholder:
329
+ * en: "Enter your email"
330
+ * ja: "メールアドレスを入力"
331
+ * vi: "Nhập email của bạn"
332
+ */
333
+ readonly placeholder?: LocalizedString;
287
334
  /**
288
335
  * Per-field settings for compound types.
289
336
  * Allows overriding nullable, hidden, fillable for specific expanded fields.
@@ -309,6 +356,34 @@ interface CompoundFieldOverride {
309
356
  readonly hidden?: boolean;
310
357
  /** Override fillable for this field */
311
358
  readonly fillable?: boolean;
359
+ /** Override length for string fields */
360
+ readonly length?: number;
361
+ /**
362
+ * Override display name (label) for this field (supports multi-language).
363
+ * If not specified, uses the default label from the plugin type definition.
364
+ *
365
+ * @example
366
+ * fields:
367
+ * Lastname:
368
+ * displayName:
369
+ * en: "Last Name"
370
+ * ja: "姓"
371
+ * vi: "Họ"
372
+ */
373
+ readonly displayName?: LocalizedString;
374
+ /**
375
+ * Override placeholder for this field (supports multi-language).
376
+ * If not specified, uses the default placeholder from the plugin type definition.
377
+ *
378
+ * @example
379
+ * fields:
380
+ * Lastname:
381
+ * placeholder:
382
+ * en: "e.g. Yamada"
383
+ * ja: "例:山田"
384
+ * vi: "VD: Nguyễn"
385
+ */
386
+ readonly placeholder?: LocalizedString;
312
387
  }
313
388
  /**
314
389
  * String property with length constraint.
@@ -518,6 +593,24 @@ interface TypeScriptTypeInfo {
518
593
  readonly name: string;
519
594
  };
520
595
  }
596
+ /**
597
+ * Validation rules for a field.
598
+ * These are applied at the Zod schema level.
599
+ */
600
+ interface FieldValidationRules {
601
+ /** Minimum length for strings */
602
+ readonly minLength?: number;
603
+ /** Maximum length for strings (defaults to sql.length if not set) */
604
+ readonly maxLength?: number;
605
+ /** Minimum value for numbers */
606
+ readonly min?: number;
607
+ /** Maximum value for numbers */
608
+ readonly max?: number;
609
+ /** Regex pattern for string validation */
610
+ readonly pattern?: string;
611
+ /** Built-in format validators */
612
+ readonly format?: 'email' | 'url' | 'phone' | 'postal_code';
613
+ }
521
614
  /**
522
615
  * Expanded field definition for compound types.
523
616
  * Used when a single custom type expands to multiple database columns.
@@ -535,6 +628,42 @@ interface ExpandedFieldDefinition {
535
628
  readonly cast?: string;
536
629
  /** Default nullable for this field (can be overridden in schema) */
537
630
  readonly nullable?: boolean;
631
+ /**
632
+ * Validation rules for this field.
633
+ * Can be overridden in schema via fields.{suffix}.rules
634
+ *
635
+ * @example
636
+ * rules: {
637
+ * minLength: 1,
638
+ * maxLength: 100,
639
+ * pattern: '^[ァ-ヶー]+$' // Katakana only
640
+ * }
641
+ */
642
+ readonly rules?: FieldValidationRules;
643
+ /**
644
+ * Default label for this field (supports multi-language).
645
+ * Can be overridden in schema via fields.{suffix}.displayName
646
+ *
647
+ * @example
648
+ * label: {
649
+ * en: "Last Name",
650
+ * ja: "姓",
651
+ * vi: "Họ"
652
+ * }
653
+ */
654
+ readonly label?: LocalizedString;
655
+ /**
656
+ * Default placeholder for this field (supports multi-language).
657
+ * Can be overridden in schema via fields.{suffix}.placeholder
658
+ *
659
+ * @example
660
+ * placeholder: {
661
+ * en: "e.g. Yamada",
662
+ * ja: "例:山田",
663
+ * vi: "VD: Nguyễn"
664
+ * }
665
+ */
666
+ readonly placeholder?: LocalizedString;
538
667
  }
539
668
  /**
540
669
  * Accessor definition for compound types.
@@ -675,8 +804,8 @@ interface PluginLogger {
675
804
  interface PluginEnumDefinition {
676
805
  /** Enum name (used as schema name) */
677
806
  readonly name: string;
678
- /** Human-readable display name */
679
- readonly displayName?: string;
807
+ /** Human-readable display name (supports multi-language) */
808
+ readonly displayName?: LocalizedString;
680
809
  /** Description of the enum */
681
810
  readonly description?: string;
682
811
  /** Enum values */
@@ -688,8 +817,8 @@ interface PluginEnumDefinition {
688
817
  interface PluginEnumValue {
689
818
  /** Value stored in database */
690
819
  readonly value: string;
691
- /** Human-readable label */
692
- readonly label: string;
820
+ /** Human-readable label (supports multi-language) */
821
+ readonly label: LocalizedString;
693
822
  /** Extra properties for this enum value */
694
823
  readonly extra?: Record<string, string | number | boolean>;
695
824
  }
@@ -851,6 +980,8 @@ interface GeneratorContext {
851
980
  readonly previousOutputs: ReadonlyMap<string, readonly GeneratorOutput[]>;
852
981
  /** Custom types registered by all plugins (for compound type expansion) */
853
982
  readonly customTypes: ReadonlyMap<string, CustomTypeDefinition>;
983
+ /** Enums registered by all plugins (e.g., Prefecture, BankAccountType) */
984
+ readonly pluginEnums: ReadonlyMap<string, PluginEnumDefinition>;
854
985
  /** Locale configuration for multi-language support */
855
986
  readonly localeConfig?: LocaleConfig | undefined;
856
987
  }
@@ -980,8 +1111,26 @@ interface LaravelOutputConfig {
980
1111
  * TypeScript output configuration.
981
1112
  */
982
1113
  interface TypeScriptOutputConfig {
983
- /** Output file or directory for TypeScript types */
1114
+ /**
1115
+ * Base output directory for all TypeScript files.
1116
+ * Schemas and enums will be placed in subdirectories.
1117
+ * @example 'resources/ts/omnify'
1118
+ */
984
1119
  readonly path: string;
1120
+ /**
1121
+ * Subdirectory for schema files (interfaces, Zod schemas, i18n).
1122
+ * Relative to `path`.
1123
+ * @default 'schemas'
1124
+ * @example 'models' - places schemas at {path}/models/
1125
+ */
1126
+ readonly schemasDir?: string;
1127
+ /**
1128
+ * Subdirectory for enum files.
1129
+ * Relative to `path`.
1130
+ * @default 'enum'
1131
+ * @example 'enums' - places enums at {path}/enums/
1132
+ */
1133
+ readonly enumDir?: string;
985
1134
  /** Whether to generate a single file or multiple files */
986
1135
  readonly singleFile?: boolean;
987
1136
  /** Whether to generate enum types */
@@ -1117,4 +1266,4 @@ declare function ok<T>(value: T): Result<T, never>;
1117
1266
  */
1118
1267
  declare function err<E>(error: E): Result<never, E>;
1119
1268
 
1120
- export { type AssociationDefinition, type AssociationRelation, type BasePropertyDefinition, type BuiltInPropertyType, type ColumnChange, type CompoundFieldOverride, type CompoundTypeAccessor, type CustomTypeDefinition, DEFAULT_LOCALE_CONFIG, type DatabaseConfig, type DatabaseDriver, type EnumPropertyDefinition, type EnumRefPropertyDefinition, type ErrorCode, type ErrorLocation, type ExpandedFieldDefinition, type FilePropertyDefinition, type GeneratorContext, type GeneratorDefinition, type GeneratorOutput, type GeneratorOutputType, type IdType, type IndexChange, type IndexDefinition, type IndexSnapshot, type IndexType, type InlineEnumValue, type LaravelOutputConfig, type LoadedSchema, type LocaleCode, type LocaleConfig, type LocaleMap, type LocaleResolutionOptions, type LocalizedString, type NumericPropertyDefinition, type OmnifyConfig, type OmnifyErrorInfo, type OmnifyPlugin, type OutputConfig, type PivotFieldDefinition, type PluginConfigField, type PluginConfigFieldType, type PluginConfigSchema, type PluginConfigSelectOption, type PluginContext, type PluginEnumDefinition, type PluginEnumValue, type PluginFactory, type PluginLogger, type PropertyDefinition, type PropertySnapshot, type PropertyType, type ReferentialAction, type ResolvedOmnifyConfig, type Result, type SchemaChange, type SchemaChangeType, type SchemaCollection, type SchemaDefinition, type SchemaKind, type SchemaOptions, type SqlColumnDefinition, type StringPropertyDefinition, type TypeScriptOutputConfig, type TypeScriptTypeInfo, type ValidationRules, err, getAvailableLocales, isLocaleMap, isLocalizedString, mergeLocalizedStrings, ok, resolveLocalizedString, toLocaleMap };
1269
+ export { type AssociationDefinition, type AssociationRelation, type BasePropertyDefinition, type BuiltInPropertyType, type ColumnChange, type CompoundFieldOverride, type CompoundTypeAccessor, type CustomTypeDefinition, DEFAULT_LOCALE_CONFIG, type DatabaseConfig, type DatabaseDriver, type EnumPropertyDefinition, type EnumRefPropertyDefinition, type ErrorCode, type ErrorLocation, type ExpandedFieldDefinition, type FieldValidationRules, type FilePropertyDefinition, type GeneratorContext, type GeneratorDefinition, type GeneratorOutput, type GeneratorOutputType, type IdType, type IndexChange, type IndexDefinition, type IndexSnapshot, type IndexType, type InlineEnumValue, type LaravelOutputConfig, type LoadedSchema, type LocaleCode, type LocaleConfig, type LocaleMap, type LocaleResolutionOptions, type LocalizedString, type NumericPropertyDefinition, type OmnifyConfig, type OmnifyErrorInfo, type OmnifyPlugin, type OutputConfig, type PivotFieldDefinition, type PluginConfigField, type PluginConfigFieldType, type PluginConfigSchema, type PluginConfigSelectOption, type PluginContext, type PluginEnumDefinition, type PluginEnumValue, type PluginFactory, type PluginLogger, type PropertyDefinition, type PropertySnapshot, type PropertyType, type ReferentialAction, type ResolvedOmnifyConfig, type Result, type SchemaChange, type SchemaChangeType, type SchemaCollection, type SchemaDefinition, type SchemaKind, type SchemaOptions, type SqlColumnDefinition, type StringPropertyDefinition, type TypeScriptOutputConfig, type TypeScriptTypeInfo, type ValidationMessages, type ValidationRules, err, getAvailableLocales, isLocaleMap, isLocalizedString, mergeLocalizedStrings, ok, resolveLocalizedString, toLocaleMap };
package/dist/index.d.ts CHANGED
@@ -29,6 +29,21 @@ type LocaleMap = Readonly<Record<LocaleCode, string>>;
29
29
  * vi: "Tên người dùng"
30
30
  */
31
31
  type LocalizedString = string | LocaleMap;
32
+ /**
33
+ * Validation message templates.
34
+ * Use placeholders: ${displayName}, ${min}, ${max}, ${length}, ${pattern}
35
+ */
36
+ interface ValidationMessages {
37
+ readonly required?: LocaleMap;
38
+ readonly minLength?: LocaleMap;
39
+ readonly maxLength?: LocaleMap;
40
+ readonly min?: LocaleMap;
41
+ readonly max?: LocaleMap;
42
+ readonly email?: LocaleMap;
43
+ readonly url?: LocaleMap;
44
+ readonly pattern?: LocaleMap;
45
+ readonly [key: string]: LocaleMap | undefined;
46
+ }
32
47
  /**
33
48
  * Locale configuration for omnify projects.
34
49
  * Configured in .omnify.yaml or omnify.config.ts
@@ -53,6 +68,24 @@ interface LocaleConfig {
53
68
  * @example 'en' - If 'ko' not found, try 'en' before defaultLocale
54
69
  */
55
70
  readonly fallbackLocale?: LocaleCode;
71
+ /**
72
+ * Custom validation messages.
73
+ * Override default messages or add new languages.
74
+ *
75
+ * @example
76
+ * messages: {
77
+ * required: {
78
+ * ja: '${displayName}は必須です',
79
+ * en: '${displayName} is required',
80
+ * ko: '${displayName}은(는) 필수입니다',
81
+ * },
82
+ * maxLength: {
83
+ * ja: '${displayName}は${max}文字以内で入力してください',
84
+ * en: '${displayName} must be at most ${max} characters',
85
+ * },
86
+ * }
87
+ */
88
+ readonly messages?: ValidationMessages;
56
89
  }
57
90
  /**
58
91
  * Default locale configuration.
@@ -284,6 +317,20 @@ interface BasePropertyDefinition {
284
317
  * @default true (all non-association fields are fillable by default)
285
318
  */
286
319
  readonly fillable?: boolean;
320
+ /**
321
+ * Placeholder text for form inputs (supports multi-language).
322
+ * Used in frontend forms to show hint text before user input.
323
+ *
324
+ * @example
325
+ * placeholder: "Enter your email"
326
+ *
327
+ * @example
328
+ * placeholder:
329
+ * en: "Enter your email"
330
+ * ja: "メールアドレスを入力"
331
+ * vi: "Nhập email của bạn"
332
+ */
333
+ readonly placeholder?: LocalizedString;
287
334
  /**
288
335
  * Per-field settings for compound types.
289
336
  * Allows overriding nullable, hidden, fillable for specific expanded fields.
@@ -309,6 +356,34 @@ interface CompoundFieldOverride {
309
356
  readonly hidden?: boolean;
310
357
  /** Override fillable for this field */
311
358
  readonly fillable?: boolean;
359
+ /** Override length for string fields */
360
+ readonly length?: number;
361
+ /**
362
+ * Override display name (label) for this field (supports multi-language).
363
+ * If not specified, uses the default label from the plugin type definition.
364
+ *
365
+ * @example
366
+ * fields:
367
+ * Lastname:
368
+ * displayName:
369
+ * en: "Last Name"
370
+ * ja: "姓"
371
+ * vi: "Họ"
372
+ */
373
+ readonly displayName?: LocalizedString;
374
+ /**
375
+ * Override placeholder for this field (supports multi-language).
376
+ * If not specified, uses the default placeholder from the plugin type definition.
377
+ *
378
+ * @example
379
+ * fields:
380
+ * Lastname:
381
+ * placeholder:
382
+ * en: "e.g. Yamada"
383
+ * ja: "例:山田"
384
+ * vi: "VD: Nguyễn"
385
+ */
386
+ readonly placeholder?: LocalizedString;
312
387
  }
313
388
  /**
314
389
  * String property with length constraint.
@@ -518,6 +593,24 @@ interface TypeScriptTypeInfo {
518
593
  readonly name: string;
519
594
  };
520
595
  }
596
+ /**
597
+ * Validation rules for a field.
598
+ * These are applied at the Zod schema level.
599
+ */
600
+ interface FieldValidationRules {
601
+ /** Minimum length for strings */
602
+ readonly minLength?: number;
603
+ /** Maximum length for strings (defaults to sql.length if not set) */
604
+ readonly maxLength?: number;
605
+ /** Minimum value for numbers */
606
+ readonly min?: number;
607
+ /** Maximum value for numbers */
608
+ readonly max?: number;
609
+ /** Regex pattern for string validation */
610
+ readonly pattern?: string;
611
+ /** Built-in format validators */
612
+ readonly format?: 'email' | 'url' | 'phone' | 'postal_code';
613
+ }
521
614
  /**
522
615
  * Expanded field definition for compound types.
523
616
  * Used when a single custom type expands to multiple database columns.
@@ -535,6 +628,42 @@ interface ExpandedFieldDefinition {
535
628
  readonly cast?: string;
536
629
  /** Default nullable for this field (can be overridden in schema) */
537
630
  readonly nullable?: boolean;
631
+ /**
632
+ * Validation rules for this field.
633
+ * Can be overridden in schema via fields.{suffix}.rules
634
+ *
635
+ * @example
636
+ * rules: {
637
+ * minLength: 1,
638
+ * maxLength: 100,
639
+ * pattern: '^[ァ-ヶー]+$' // Katakana only
640
+ * }
641
+ */
642
+ readonly rules?: FieldValidationRules;
643
+ /**
644
+ * Default label for this field (supports multi-language).
645
+ * Can be overridden in schema via fields.{suffix}.displayName
646
+ *
647
+ * @example
648
+ * label: {
649
+ * en: "Last Name",
650
+ * ja: "姓",
651
+ * vi: "Họ"
652
+ * }
653
+ */
654
+ readonly label?: LocalizedString;
655
+ /**
656
+ * Default placeholder for this field (supports multi-language).
657
+ * Can be overridden in schema via fields.{suffix}.placeholder
658
+ *
659
+ * @example
660
+ * placeholder: {
661
+ * en: "e.g. Yamada",
662
+ * ja: "例:山田",
663
+ * vi: "VD: Nguyễn"
664
+ * }
665
+ */
666
+ readonly placeholder?: LocalizedString;
538
667
  }
539
668
  /**
540
669
  * Accessor definition for compound types.
@@ -675,8 +804,8 @@ interface PluginLogger {
675
804
  interface PluginEnumDefinition {
676
805
  /** Enum name (used as schema name) */
677
806
  readonly name: string;
678
- /** Human-readable display name */
679
- readonly displayName?: string;
807
+ /** Human-readable display name (supports multi-language) */
808
+ readonly displayName?: LocalizedString;
680
809
  /** Description of the enum */
681
810
  readonly description?: string;
682
811
  /** Enum values */
@@ -688,8 +817,8 @@ interface PluginEnumDefinition {
688
817
  interface PluginEnumValue {
689
818
  /** Value stored in database */
690
819
  readonly value: string;
691
- /** Human-readable label */
692
- readonly label: string;
820
+ /** Human-readable label (supports multi-language) */
821
+ readonly label: LocalizedString;
693
822
  /** Extra properties for this enum value */
694
823
  readonly extra?: Record<string, string | number | boolean>;
695
824
  }
@@ -851,6 +980,8 @@ interface GeneratorContext {
851
980
  readonly previousOutputs: ReadonlyMap<string, readonly GeneratorOutput[]>;
852
981
  /** Custom types registered by all plugins (for compound type expansion) */
853
982
  readonly customTypes: ReadonlyMap<string, CustomTypeDefinition>;
983
+ /** Enums registered by all plugins (e.g., Prefecture, BankAccountType) */
984
+ readonly pluginEnums: ReadonlyMap<string, PluginEnumDefinition>;
854
985
  /** Locale configuration for multi-language support */
855
986
  readonly localeConfig?: LocaleConfig | undefined;
856
987
  }
@@ -980,8 +1111,26 @@ interface LaravelOutputConfig {
980
1111
  * TypeScript output configuration.
981
1112
  */
982
1113
  interface TypeScriptOutputConfig {
983
- /** Output file or directory for TypeScript types */
1114
+ /**
1115
+ * Base output directory for all TypeScript files.
1116
+ * Schemas and enums will be placed in subdirectories.
1117
+ * @example 'resources/ts/omnify'
1118
+ */
984
1119
  readonly path: string;
1120
+ /**
1121
+ * Subdirectory for schema files (interfaces, Zod schemas, i18n).
1122
+ * Relative to `path`.
1123
+ * @default 'schemas'
1124
+ * @example 'models' - places schemas at {path}/models/
1125
+ */
1126
+ readonly schemasDir?: string;
1127
+ /**
1128
+ * Subdirectory for enum files.
1129
+ * Relative to `path`.
1130
+ * @default 'enum'
1131
+ * @example 'enums' - places enums at {path}/enums/
1132
+ */
1133
+ readonly enumDir?: string;
985
1134
  /** Whether to generate a single file or multiple files */
986
1135
  readonly singleFile?: boolean;
987
1136
  /** Whether to generate enum types */
@@ -1117,4 +1266,4 @@ declare function ok<T>(value: T): Result<T, never>;
1117
1266
  */
1118
1267
  declare function err<E>(error: E): Result<never, E>;
1119
1268
 
1120
- export { type AssociationDefinition, type AssociationRelation, type BasePropertyDefinition, type BuiltInPropertyType, type ColumnChange, type CompoundFieldOverride, type CompoundTypeAccessor, type CustomTypeDefinition, DEFAULT_LOCALE_CONFIG, type DatabaseConfig, type DatabaseDriver, type EnumPropertyDefinition, type EnumRefPropertyDefinition, type ErrorCode, type ErrorLocation, type ExpandedFieldDefinition, type FilePropertyDefinition, type GeneratorContext, type GeneratorDefinition, type GeneratorOutput, type GeneratorOutputType, type IdType, type IndexChange, type IndexDefinition, type IndexSnapshot, type IndexType, type InlineEnumValue, type LaravelOutputConfig, type LoadedSchema, type LocaleCode, type LocaleConfig, type LocaleMap, type LocaleResolutionOptions, type LocalizedString, type NumericPropertyDefinition, type OmnifyConfig, type OmnifyErrorInfo, type OmnifyPlugin, type OutputConfig, type PivotFieldDefinition, type PluginConfigField, type PluginConfigFieldType, type PluginConfigSchema, type PluginConfigSelectOption, type PluginContext, type PluginEnumDefinition, type PluginEnumValue, type PluginFactory, type PluginLogger, type PropertyDefinition, type PropertySnapshot, type PropertyType, type ReferentialAction, type ResolvedOmnifyConfig, type Result, type SchemaChange, type SchemaChangeType, type SchemaCollection, type SchemaDefinition, type SchemaKind, type SchemaOptions, type SqlColumnDefinition, type StringPropertyDefinition, type TypeScriptOutputConfig, type TypeScriptTypeInfo, type ValidationRules, err, getAvailableLocales, isLocaleMap, isLocalizedString, mergeLocalizedStrings, ok, resolveLocalizedString, toLocaleMap };
1269
+ export { type AssociationDefinition, type AssociationRelation, type BasePropertyDefinition, type BuiltInPropertyType, type ColumnChange, type CompoundFieldOverride, type CompoundTypeAccessor, type CustomTypeDefinition, DEFAULT_LOCALE_CONFIG, type DatabaseConfig, type DatabaseDriver, type EnumPropertyDefinition, type EnumRefPropertyDefinition, type ErrorCode, type ErrorLocation, type ExpandedFieldDefinition, type FieldValidationRules, type FilePropertyDefinition, type GeneratorContext, type GeneratorDefinition, type GeneratorOutput, type GeneratorOutputType, type IdType, type IndexChange, type IndexDefinition, type IndexSnapshot, type IndexType, type InlineEnumValue, type LaravelOutputConfig, type LoadedSchema, type LocaleCode, type LocaleConfig, type LocaleMap, type LocaleResolutionOptions, type LocalizedString, type NumericPropertyDefinition, type OmnifyConfig, type OmnifyErrorInfo, type OmnifyPlugin, type OutputConfig, type PivotFieldDefinition, type PluginConfigField, type PluginConfigFieldType, type PluginConfigSchema, type PluginConfigSelectOption, type PluginContext, type PluginEnumDefinition, type PluginEnumValue, type PluginFactory, type PluginLogger, type PropertyDefinition, type PropertySnapshot, type PropertyType, type ReferentialAction, type ResolvedOmnifyConfig, type Result, type SchemaChange, type SchemaChangeType, type SchemaCollection, type SchemaDefinition, type SchemaKind, type SchemaOptions, type SqlColumnDefinition, type StringPropertyDefinition, type TypeScriptOutputConfig, type TypeScriptTypeInfo, type ValidationMessages, type ValidationRules, err, getAvailableLocales, isLocaleMap, isLocalizedString, mergeLocalizedStrings, ok, resolveLocalizedString, toLocaleMap };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/i18n.ts"],"sourcesContent":["/**\n * @famgia/omnify-types - Error Types\n *\n * Type definitions for omnify error handling.\n * Errors follow the format: file:line + message + suggestion\n */\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/**\n * Error code categories:\n * - E0xx: Configuration errors\n * - E1xx: Schema parsing errors\n * - E2xx: Schema validation errors\n * - E3xx: Plugin errors\n * - E4xx: Atlas/Database errors\n * - E5xx: Generation errors\n * - E9xx: Internal errors\n */\nexport type ErrorCode =\n // Configuration errors (E0xx)\n | 'E001' // Config file not found\n | 'E002' // Invalid config format\n | 'E003' // Missing required config field\n | 'E004' // Invalid database driver\n | 'E005' // Invalid output path\n // Schema parsing errors (E1xx)\n | 'E101' // Schema file not found\n | 'E102' // Invalid YAML syntax\n | 'E103' // Invalid JSON syntax\n | 'E104' // Empty schema file\n | 'E105' // Schema read error\n // Schema validation errors (E2xx)\n | 'E201' // Invalid property type\n | 'E202' // Missing required field\n | 'E203' // Invalid association target\n | 'E204' // Circular reference detected\n | 'E205' // Duplicate schema name\n | 'E206' // Invalid schema options\n | 'E207' // Invalid enum values\n | 'E208' // Orphaned inverse relation\n // Plugin errors (E3xx)\n | 'E301' // Plugin not found\n | 'E302' // Plugin load error\n | 'E303' // Invalid plugin format\n | 'E304' // Plugin type conflict\n | 'E305' // Plugin setup error\n // Atlas/Database errors (E4xx)\n | 'E401' // Atlas CLI not found\n | 'E402' // Atlas diff failed\n | 'E403' // Database connection error\n | 'E404' // HCL generation error\n | 'E405' // Lock file error\n // Generation errors (E5xx)\n | 'E501' // Migration generation failed\n | 'E502' // TypeScript generation failed\n | 'E503' // Output write error\n | 'E504' // Template error\n // Internal errors (E9xx)\n | 'E901' // Unexpected error\n | 'E902'; // Not implemented\n\n// ============================================================================\n// Error Info\n// ============================================================================\n\n/**\n * Source location where an error occurred.\n */\nexport interface ErrorLocation {\n /** File path where the error occurred */\n readonly file?: string;\n /** Line number (1-based) */\n readonly line?: number;\n /** Column number (1-based) */\n readonly column?: number;\n}\n\n/**\n * Structured error information for omnify errors.\n */\nexport interface OmnifyErrorInfo {\n /** Error code (e.g., 'E201') */\n readonly code: ErrorCode;\n /** Human-readable error message */\n readonly message: string;\n /** Location where the error occurred */\n readonly location?: ErrorLocation;\n /** Suggested fix for the error */\n readonly suggestion?: string;\n /** Additional context or details */\n readonly details?: Record<string, unknown>;\n /** Original error if this wraps another error */\n readonly cause?: Error;\n}\n\n/**\n * Result type for operations that may fail.\n */\nexport type Result<T, E = OmnifyErrorInfo> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: E };\n\n/**\n * Helper to create a successful result.\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Helper to create an error result.\n */\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n","/**\n * @famgia/omnify-types - Internationalization (i18n) Types\n *\n * Types for multi-language support in schema definitions.\n * Supports displayName, description, and other localizable strings.\n */\n\n// ============================================================================\n// Locale Types\n// ============================================================================\n\n/**\n * Supported locale code format (ISO 639-1 language codes).\n * Examples: 'en', 'ja', 'vi', 'zh', 'ko', 'fr', 'de', 'es', 'pt'\n */\nexport type LocaleCode = string;\n\n/**\n * Map of locale codes to localized strings.\n * @example { en: 'User Name', ja: 'ユーザー名', vi: 'Tên người dùng' }\n */\nexport type LocaleMap = Readonly<Record<LocaleCode, string>>;\n\n/**\n * Localized string - can be either a simple string (default locale)\n * or a map of locale codes to localized strings.\n *\n * @example\n * // Simple string (uses default locale)\n * displayName: \"User Name\"\n *\n * // Multi-language object\n * displayName:\n * en: \"User Name\"\n * ja: \"ユーザー名\"\n * vi: \"Tên người dùng\"\n */\nexport type LocalizedString = string | LocaleMap;\n\n// ============================================================================\n// Locale Configuration\n// ============================================================================\n\n/**\n * Locale configuration for omnify projects.\n * Configured in .omnify.yaml or omnify.config.ts\n */\nexport interface LocaleConfig {\n /**\n * List of supported locale codes.\n * @default ['en']\n * @example ['en', 'ja', 'vi']\n */\n readonly locales: readonly LocaleCode[];\n\n /**\n * Default locale code used when:\n * - displayName is a simple string\n * - Requested locale is not found and no fallback matches\n * @default 'en'\n */\n readonly defaultLocale: LocaleCode;\n\n /**\n * Fallback locale code used when requested locale is not found.\n * If not set, falls back to defaultLocale.\n * @example 'en' - If 'ko' not found, try 'en' before defaultLocale\n */\n readonly fallbackLocale?: LocaleCode;\n}\n\n/**\n * Default locale configuration.\n */\nexport const DEFAULT_LOCALE_CONFIG: LocaleConfig = {\n locales: ['en'],\n defaultLocale: 'en',\n};\n\n// ============================================================================\n// Resolution Options\n// ============================================================================\n\n/**\n * Options for resolving localized strings.\n */\nexport interface LocaleResolutionOptions {\n /**\n * Locale to resolve to.\n * If not provided, uses defaultLocale from config.\n */\n readonly locale?: LocaleCode;\n\n /**\n * Locale configuration.\n * If not provided, uses DEFAULT_LOCALE_CONFIG.\n */\n readonly config?: LocaleConfig;\n\n /**\n * Whether to throw an error if locale is not found.\n * If false, returns undefined for missing locales.\n * @default false\n */\n readonly strict?: boolean;\n}\n\n// ============================================================================\n// Utility Type Guards\n// ============================================================================\n\n/**\n * Check if a value is a LocaleMap (object with locale keys).\n */\nexport function isLocaleMap(value: unknown): value is LocaleMap {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n // Check that all values are strings\n return Object.values(value).every(v => typeof v === 'string');\n}\n\n/**\n * Check if a value is a LocalizedString.\n */\nexport function isLocalizedString(value: unknown): value is LocalizedString {\n return typeof value === 'string' || isLocaleMap(value);\n}\n\n// ============================================================================\n// Resolution Functions\n// ============================================================================\n\n/**\n * Resolve a localized string to a specific locale.\n *\n * Resolution order:\n * 1. Requested locale (if provided)\n * 2. Fallback locale (if configured)\n * 3. Default locale\n * 4. First available locale (if none of the above match)\n * 5. undefined (if strict=false) or throws error (if strict=true)\n *\n * @param value - The localized string value\n * @param options - Resolution options\n * @returns The resolved string or undefined\n *\n * @example\n * // Simple string\n * resolveLocalizedString('Hello') // => 'Hello'\n *\n * // Multi-language with locale\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ja' })\n * // => 'こんにちは'\n *\n * // Multi-language with fallback\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ko' })\n * // => 'Hello' (falls back to defaultLocale)\n */\nexport function resolveLocalizedString(\n value: LocalizedString | undefined | null,\n options: LocaleResolutionOptions = {}\n): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n // Simple string - return as-is\n if (typeof value === 'string') {\n return value;\n }\n\n const config = options.config ?? DEFAULT_LOCALE_CONFIG;\n const requestedLocale = options.locale ?? config.defaultLocale;\n\n // Try requested locale\n if (requestedLocale in value) {\n return value[requestedLocale];\n }\n\n // Try fallback locale\n if (config.fallbackLocale && config.fallbackLocale in value) {\n return value[config.fallbackLocale];\n }\n\n // Try default locale\n if (config.defaultLocale in value) {\n return value[config.defaultLocale];\n }\n\n // Try first available locale\n const availableLocales = Object.keys(value);\n const firstLocale = availableLocales[0];\n if (firstLocale !== undefined) {\n return value[firstLocale];\n }\n\n // No locale found\n if (options.strict) {\n throw new Error(\n `Locale '${requestedLocale}' not found in localized string. ` +\n `Available locales: none`\n );\n }\n\n return undefined;\n}\n\n/**\n * Get all available locales from a localized string.\n *\n * @param value - The localized string value\n * @returns Array of available locale codes, or ['default'] for simple strings\n */\nexport function getAvailableLocales(\n value: LocalizedString | undefined | null\n): readonly LocaleCode[] {\n if (value === undefined || value === null) {\n return [];\n }\n\n if (typeof value === 'string') {\n return ['default'];\n }\n\n return Object.keys(value);\n}\n\n/**\n * Convert a localized string to a full LocaleMap.\n * Simple strings are converted to { [defaultLocale]: value }.\n *\n * @param value - The localized string value\n * @param defaultLocale - Default locale for simple strings\n * @returns LocaleMap or undefined\n */\nexport function toLocaleMap(\n value: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n if (typeof value === 'string') {\n return { [defaultLocale]: value };\n }\n\n return value;\n}\n\n/**\n * Merge two localized strings, with the second taking precedence.\n *\n * @param base - Base localized string\n * @param override - Override localized string\n * @param defaultLocale - Default locale for simple strings\n * @returns Merged LocaleMap\n */\nexport function mergeLocalizedStrings(\n base: LocalizedString | undefined | null,\n override: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n const baseMap = toLocaleMap(base, defaultLocale);\n const overrideMap = toLocaleMap(override, defaultLocale);\n\n if (!baseMap && !overrideMap) {\n return undefined;\n }\n\n return {\n ...baseMap,\n ...overrideMap,\n };\n}\n"],"mappings":";AA4GO,SAAS,GAAM,OAA4B;AAChD,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAKO,SAAS,IAAO,OAA4B;AACjD,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;;;AC3CO,IAAM,wBAAsC;AAAA,EACjD,SAAS,CAAC,IAAI;AAAA,EACd,eAAe;AACjB;AAqCO,SAAS,YAAY,OAAoC;AAC9D,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,OAAO,KAAK,EAAE,MAAM,OAAK,OAAO,MAAM,QAAQ;AAC9D;AAKO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,OAAO,UAAU,YAAY,YAAY,KAAK;AACvD;AAgCO,SAAS,uBACd,OACA,UAAmC,CAAC,GAChB;AACpB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,kBAAkB,QAAQ,UAAU,OAAO;AAGjD,MAAI,mBAAmB,OAAO;AAC5B,WAAO,MAAM,eAAe;AAAA,EAC9B;AAGA,MAAI,OAAO,kBAAkB,OAAO,kBAAkB,OAAO;AAC3D,WAAO,MAAM,OAAO,cAAc;AAAA,EACpC;AAGA,MAAI,OAAO,iBAAiB,OAAO;AACjC,WAAO,MAAM,OAAO,aAAa;AAAA,EACnC;AAGA,QAAM,mBAAmB,OAAO,KAAK,KAAK;AAC1C,QAAM,cAAc,iBAAiB,CAAC;AACtC,MAAI,gBAAgB,QAAW;AAC7B,WAAO,MAAM,WAAW;AAAA,EAC1B;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR,WAAW,eAAe;AAAA,IAE5B;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,oBACd,OACuB;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,CAAC,SAAS;AAAA,EACnB;AAEA,SAAO,OAAO,KAAK,KAAK;AAC1B;AAUO,SAAS,YACd,OACA,gBAA4B,MACL;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,CAAC,aAAa,GAAG,MAAM;AAAA,EAClC;AAEA,SAAO;AACT;AAUO,SAAS,sBACd,MACA,UACA,gBAA4B,MACL;AACvB,QAAM,UAAU,YAAY,MAAM,aAAa;AAC/C,QAAM,cAAc,YAAY,UAAU,aAAa;AAEvD,MAAI,CAAC,WAAW,CAAC,aAAa;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/i18n.ts"],"sourcesContent":["/**\n * @famgia/omnify-types - Error Types\n *\n * Type definitions for omnify error handling.\n * Errors follow the format: file:line + message + suggestion\n */\n\n// ============================================================================\n// Error Codes\n// ============================================================================\n\n/**\n * Error code categories:\n * - E0xx: Configuration errors\n * - E1xx: Schema parsing errors\n * - E2xx: Schema validation errors\n * - E3xx: Plugin errors\n * - E4xx: Atlas/Database errors\n * - E5xx: Generation errors\n * - E9xx: Internal errors\n */\nexport type ErrorCode =\n // Configuration errors (E0xx)\n | 'E001' // Config file not found\n | 'E002' // Invalid config format\n | 'E003' // Missing required config field\n | 'E004' // Invalid database driver\n | 'E005' // Invalid output path\n // Schema parsing errors (E1xx)\n | 'E101' // Schema file not found\n | 'E102' // Invalid YAML syntax\n | 'E103' // Invalid JSON syntax\n | 'E104' // Empty schema file\n | 'E105' // Schema read error\n // Schema validation errors (E2xx)\n | 'E201' // Invalid property type\n | 'E202' // Missing required field\n | 'E203' // Invalid association target\n | 'E204' // Circular reference detected\n | 'E205' // Duplicate schema name\n | 'E206' // Invalid schema options\n | 'E207' // Invalid enum values\n | 'E208' // Orphaned inverse relation\n // Plugin errors (E3xx)\n | 'E301' // Plugin not found\n | 'E302' // Plugin load error\n | 'E303' // Invalid plugin format\n | 'E304' // Plugin type conflict\n | 'E305' // Plugin setup error\n // Atlas/Database errors (E4xx)\n | 'E401' // Atlas CLI not found\n | 'E402' // Atlas diff failed\n | 'E403' // Database connection error\n | 'E404' // HCL generation error\n | 'E405' // Lock file error\n // Generation errors (E5xx)\n | 'E501' // Migration generation failed\n | 'E502' // TypeScript generation failed\n | 'E503' // Output write error\n | 'E504' // Template error\n // Internal errors (E9xx)\n | 'E901' // Unexpected error\n | 'E902'; // Not implemented\n\n// ============================================================================\n// Error Info\n// ============================================================================\n\n/**\n * Source location where an error occurred.\n */\nexport interface ErrorLocation {\n /** File path where the error occurred */\n readonly file?: string;\n /** Line number (1-based) */\n readonly line?: number;\n /** Column number (1-based) */\n readonly column?: number;\n}\n\n/**\n * Structured error information for omnify errors.\n */\nexport interface OmnifyErrorInfo {\n /** Error code (e.g., 'E201') */\n readonly code: ErrorCode;\n /** Human-readable error message */\n readonly message: string;\n /** Location where the error occurred */\n readonly location?: ErrorLocation;\n /** Suggested fix for the error */\n readonly suggestion?: string;\n /** Additional context or details */\n readonly details?: Record<string, unknown>;\n /** Original error if this wraps another error */\n readonly cause?: Error;\n}\n\n/**\n * Result type for operations that may fail.\n */\nexport type Result<T, E = OmnifyErrorInfo> =\n | { readonly ok: true; readonly value: T }\n | { readonly ok: false; readonly error: E };\n\n/**\n * Helper to create a successful result.\n */\nexport function ok<T>(value: T): Result<T, never> {\n return { ok: true, value };\n}\n\n/**\n * Helper to create an error result.\n */\nexport function err<E>(error: E): Result<never, E> {\n return { ok: false, error };\n}\n","/**\n * @famgia/omnify-types - Internationalization (i18n) Types\n *\n * Types for multi-language support in schema definitions.\n * Supports displayName, description, and other localizable strings.\n */\n\n// ============================================================================\n// Locale Types\n// ============================================================================\n\n/**\n * Supported locale code format (ISO 639-1 language codes).\n * Examples: 'en', 'ja', 'vi', 'zh', 'ko', 'fr', 'de', 'es', 'pt'\n */\nexport type LocaleCode = string;\n\n/**\n * Map of locale codes to localized strings.\n * @example { en: 'User Name', ja: 'ユーザー名', vi: 'Tên người dùng' }\n */\nexport type LocaleMap = Readonly<Record<LocaleCode, string>>;\n\n/**\n * Localized string - can be either a simple string (default locale)\n * or a map of locale codes to localized strings.\n *\n * @example\n * // Simple string (uses default locale)\n * displayName: \"User Name\"\n *\n * // Multi-language object\n * displayName:\n * en: \"User Name\"\n * ja: \"ユーザー名\"\n * vi: \"Tên người dùng\"\n */\nexport type LocalizedString = string | LocaleMap;\n\n// ============================================================================\n// Locale Configuration\n// ============================================================================\n\n/**\n * Validation message templates.\n * Use placeholders: ${displayName}, ${min}, ${max}, ${length}, ${pattern}\n */\nexport interface ValidationMessages {\n readonly required?: LocaleMap;\n readonly minLength?: LocaleMap;\n readonly maxLength?: LocaleMap;\n readonly min?: LocaleMap;\n readonly max?: LocaleMap;\n readonly email?: LocaleMap;\n readonly url?: LocaleMap;\n readonly pattern?: LocaleMap;\n readonly [key: string]: LocaleMap | undefined;\n}\n\n/**\n * Locale configuration for omnify projects.\n * Configured in .omnify.yaml or omnify.config.ts\n */\nexport interface LocaleConfig {\n /**\n * List of supported locale codes.\n * @default ['en']\n * @example ['en', 'ja', 'vi']\n */\n readonly locales: readonly LocaleCode[];\n\n /**\n * Default locale code used when:\n * - displayName is a simple string\n * - Requested locale is not found and no fallback matches\n * @default 'en'\n */\n readonly defaultLocale: LocaleCode;\n\n /**\n * Fallback locale code used when requested locale is not found.\n * If not set, falls back to defaultLocale.\n * @example 'en' - If 'ko' not found, try 'en' before defaultLocale\n */\n readonly fallbackLocale?: LocaleCode;\n\n /**\n * Custom validation messages.\n * Override default messages or add new languages.\n *\n * @example\n * messages: {\n * required: {\n * ja: '${displayName}は必須です',\n * en: '${displayName} is required',\n * ko: '${displayName}은(는) 필수입니다',\n * },\n * maxLength: {\n * ja: '${displayName}は${max}文字以内で入力してください',\n * en: '${displayName} must be at most ${max} characters',\n * },\n * }\n */\n readonly messages?: ValidationMessages;\n}\n\n/**\n * Default locale configuration.\n */\nexport const DEFAULT_LOCALE_CONFIG: LocaleConfig = {\n locales: ['en'],\n defaultLocale: 'en',\n};\n\n// ============================================================================\n// Resolution Options\n// ============================================================================\n\n/**\n * Options for resolving localized strings.\n */\nexport interface LocaleResolutionOptions {\n /**\n * Locale to resolve to.\n * If not provided, uses defaultLocale from config.\n */\n readonly locale?: LocaleCode;\n\n /**\n * Locale configuration.\n * If not provided, uses DEFAULT_LOCALE_CONFIG.\n */\n readonly config?: LocaleConfig;\n\n /**\n * Whether to throw an error if locale is not found.\n * If false, returns undefined for missing locales.\n * @default false\n */\n readonly strict?: boolean;\n}\n\n// ============================================================================\n// Utility Type Guards\n// ============================================================================\n\n/**\n * Check if a value is a LocaleMap (object with locale keys).\n */\nexport function isLocaleMap(value: unknown): value is LocaleMap {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n // Check that all values are strings\n return Object.values(value).every(v => typeof v === 'string');\n}\n\n/**\n * Check if a value is a LocalizedString.\n */\nexport function isLocalizedString(value: unknown): value is LocalizedString {\n return typeof value === 'string' || isLocaleMap(value);\n}\n\n// ============================================================================\n// Resolution Functions\n// ============================================================================\n\n/**\n * Resolve a localized string to a specific locale.\n *\n * Resolution order:\n * 1. Requested locale (if provided)\n * 2. Fallback locale (if configured)\n * 3. Default locale\n * 4. First available locale (if none of the above match)\n * 5. undefined (if strict=false) or throws error (if strict=true)\n *\n * @param value - The localized string value\n * @param options - Resolution options\n * @returns The resolved string or undefined\n *\n * @example\n * // Simple string\n * resolveLocalizedString('Hello') // => 'Hello'\n *\n * // Multi-language with locale\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ja' })\n * // => 'こんにちは'\n *\n * // Multi-language with fallback\n * resolveLocalizedString({ en: 'Hello', ja: 'こんにちは' }, { locale: 'ko' })\n * // => 'Hello' (falls back to defaultLocale)\n */\nexport function resolveLocalizedString(\n value: LocalizedString | undefined | null,\n options: LocaleResolutionOptions = {}\n): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n // Simple string - return as-is\n if (typeof value === 'string') {\n return value;\n }\n\n const config = options.config ?? DEFAULT_LOCALE_CONFIG;\n const requestedLocale = options.locale ?? config.defaultLocale;\n\n // Try requested locale\n if (requestedLocale in value) {\n return value[requestedLocale];\n }\n\n // Try fallback locale\n if (config.fallbackLocale && config.fallbackLocale in value) {\n return value[config.fallbackLocale];\n }\n\n // Try default locale\n if (config.defaultLocale in value) {\n return value[config.defaultLocale];\n }\n\n // Try first available locale\n const availableLocales = Object.keys(value);\n const firstLocale = availableLocales[0];\n if (firstLocale !== undefined) {\n return value[firstLocale];\n }\n\n // No locale found\n if (options.strict) {\n throw new Error(\n `Locale '${requestedLocale}' not found in localized string. ` +\n `Available locales: none`\n );\n }\n\n return undefined;\n}\n\n/**\n * Get all available locales from a localized string.\n *\n * @param value - The localized string value\n * @returns Array of available locale codes, or ['default'] for simple strings\n */\nexport function getAvailableLocales(\n value: LocalizedString | undefined | null\n): readonly LocaleCode[] {\n if (value === undefined || value === null) {\n return [];\n }\n\n if (typeof value === 'string') {\n return ['default'];\n }\n\n return Object.keys(value);\n}\n\n/**\n * Convert a localized string to a full LocaleMap.\n * Simple strings are converted to { [defaultLocale]: value }.\n *\n * @param value - The localized string value\n * @param defaultLocale - Default locale for simple strings\n * @returns LocaleMap or undefined\n */\nexport function toLocaleMap(\n value: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n if (typeof value === 'string') {\n return { [defaultLocale]: value };\n }\n\n return value;\n}\n\n/**\n * Merge two localized strings, with the second taking precedence.\n *\n * @param base - Base localized string\n * @param override - Override localized string\n * @param defaultLocale - Default locale for simple strings\n * @returns Merged LocaleMap\n */\nexport function mergeLocalizedStrings(\n base: LocalizedString | undefined | null,\n override: LocalizedString | undefined | null,\n defaultLocale: LocaleCode = 'en'\n): LocaleMap | undefined {\n const baseMap = toLocaleMap(base, defaultLocale);\n const overrideMap = toLocaleMap(override, defaultLocale);\n\n if (!baseMap && !overrideMap) {\n return undefined;\n }\n\n return {\n ...baseMap,\n ...overrideMap,\n };\n}\n"],"mappings":";AA4GO,SAAS,GAAM,OAA4B;AAChD,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;AAKO,SAAS,IAAO,OAA4B;AACjD,SAAO,EAAE,IAAI,OAAO,MAAM;AAC5B;;;ACRO,IAAM,wBAAsC;AAAA,EACjD,SAAS,CAAC,IAAI;AAAA,EACd,eAAe;AACjB;AAqCO,SAAS,YAAY,OAAoC;AAC9D,MAAI,OAAO,UAAU,YAAY,UAAU,QAAQ,MAAM,QAAQ,KAAK,GAAG;AACvE,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,OAAO,KAAK,EAAE,MAAM,OAAK,OAAO,MAAM,QAAQ;AAC9D;AAKO,SAAS,kBAAkB,OAA0C;AAC1E,SAAO,OAAO,UAAU,YAAY,YAAY,KAAK;AACvD;AAgCO,SAAS,uBACd,OACA,UAAmC,CAAC,GAChB;AACpB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,kBAAkB,QAAQ,UAAU,OAAO;AAGjD,MAAI,mBAAmB,OAAO;AAC5B,WAAO,MAAM,eAAe;AAAA,EAC9B;AAGA,MAAI,OAAO,kBAAkB,OAAO,kBAAkB,OAAO;AAC3D,WAAO,MAAM,OAAO,cAAc;AAAA,EACpC;AAGA,MAAI,OAAO,iBAAiB,OAAO;AACjC,WAAO,MAAM,OAAO,aAAa;AAAA,EACnC;AAGA,QAAM,mBAAmB,OAAO,KAAK,KAAK;AAC1C,QAAM,cAAc,iBAAiB,CAAC;AACtC,MAAI,gBAAgB,QAAW;AAC7B,WAAO,MAAM,WAAW;AAAA,EAC1B;AAGA,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI;AAAA,MACR,WAAW,eAAe;AAAA,IAE5B;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,oBACd,OACuB;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,CAAC,SAAS;AAAA,EACnB;AAEA,SAAO,OAAO,KAAK,KAAK;AAC1B;AAUO,SAAS,YACd,OACA,gBAA4B,MACL;AACvB,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO,EAAE,CAAC,aAAa,GAAG,MAAM;AAAA,EAClC;AAEA,SAAO;AACT;AAUO,SAAS,sBACd,MACA,UACA,gBAA4B,MACL;AACvB,QAAM,UAAU,YAAY,MAAM,aAAa;AAC/C,QAAM,cAAc,YAAY,UAAU,aAAa;AAEvD,MAAI,CAAC,WAAW,CAAC,aAAa;AAC5B,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG;AAAA,EACL;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@famgia/omnify-types",
3
- "version": "0.0.77",
3
+ "version": "0.0.78",
4
4
  "description": "Shared TypeScript types for omnify-schema",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -11,7 +11,10 @@
11
11
  },
12
12
  "kind": {
13
13
  "type": "string",
14
- "enum": ["object", "enum"],
14
+ "enum": [
15
+ "object",
16
+ "enum"
17
+ ],
15
18
  "default": "object",
16
19
  "description": "Schema kind - 'object' for database tables, 'enum' for enumeration types"
17
20
  },
@@ -41,8 +44,12 @@
41
44
  "type": "array",
42
45
  "items": {
43
46
  "oneOf": [
44
- { "type": "string" },
45
- { "$ref": "#/definitions/EnumValue" }
47
+ {
48
+ "type": "string"
49
+ },
50
+ {
51
+ "$ref": "#/definitions/EnumValue"
52
+ }
46
53
  ]
47
54
  },
48
55
  "description": "Enum values (only when kind is 'enum')"
@@ -51,10 +58,14 @@
51
58
  "definitions": {
52
59
  "LocalizedString": {
53
60
  "oneOf": [
54
- { "type": "string" },
61
+ {
62
+ "type": "string"
63
+ },
55
64
  {
56
65
  "type": "object",
57
- "additionalProperties": { "type": "string" },
66
+ "additionalProperties": {
67
+ "type": "string"
68
+ },
58
69
  "description": "Locale map (e.g., { ja: '日本語', en: 'English', vi: 'Tiếng Việt' })"
59
70
  }
60
71
  ],
@@ -70,7 +81,12 @@
70
81
  },
71
82
  "idType": {
72
83
  "type": "string",
73
- "enum": ["BigInt", "Int", "Uuid", "String"],
84
+ "enum": [
85
+ "BigInt",
86
+ "Int",
87
+ "Uuid",
88
+ "String"
89
+ ],
74
90
  "default": "BigInt",
75
91
  "description": "Type of the auto-generated 'id' column"
76
92
  },
@@ -95,7 +111,9 @@
95
111
  },
96
112
  "indexes": {
97
113
  "type": "array",
98
- "items": { "$ref": "#/definitions/IndexDefinition" },
114
+ "items": {
115
+ "$ref": "#/definitions/IndexDefinition"
116
+ },
99
117
  "description": "Database indexes for query optimization"
100
118
  },
101
119
  "hidden": {
@@ -107,11 +125,15 @@
107
125
  },
108
126
  "IndexDefinition": {
109
127
  "type": "object",
110
- "required": ["columns"],
128
+ "required": [
129
+ "columns"
130
+ ],
111
131
  "properties": {
112
132
  "columns": {
113
133
  "type": "array",
114
- "items": { "type": "string" },
134
+ "items": {
135
+ "type": "string"
136
+ },
115
137
  "description": "Columns to include in the index"
116
138
  },
117
139
  "unique": {
@@ -125,24 +147,53 @@
125
147
  },
126
148
  "type": {
127
149
  "type": "string",
128
- "enum": ["btree", "hash", "fulltext", "spatial", "gin", "gist"],
150
+ "enum": [
151
+ "btree",
152
+ "hash",
153
+ "fulltext",
154
+ "spatial",
155
+ "gin",
156
+ "gist"
157
+ ],
129
158
  "description": "Index type"
130
159
  }
131
160
  }
132
161
  },
133
162
  "PropertyDefinition": {
134
163
  "oneOf": [
135
- { "$ref": "#/definitions/StringProperty" },
136
- { "$ref": "#/definitions/NumericProperty" },
137
- { "$ref": "#/definitions/BooleanProperty" },
138
- { "$ref": "#/definitions/TextProperty" },
139
- { "$ref": "#/definitions/DateTimeProperty" },
140
- { "$ref": "#/definitions/TimestampProperty" },
141
- { "$ref": "#/definitions/JsonProperty" },
142
- { "$ref": "#/definitions/EnumProperty" },
143
- { "$ref": "#/definitions/EnumRefProperty" },
144
- { "$ref": "#/definitions/FileProperty" },
145
- { "$ref": "#/definitions/AssociationProperty" }
164
+ {
165
+ "$ref": "#/definitions/StringProperty"
166
+ },
167
+ {
168
+ "$ref": "#/definitions/NumericProperty"
169
+ },
170
+ {
171
+ "$ref": "#/definitions/BooleanProperty"
172
+ },
173
+ {
174
+ "$ref": "#/definitions/TextProperty"
175
+ },
176
+ {
177
+ "$ref": "#/definitions/DateTimeProperty"
178
+ },
179
+ {
180
+ "$ref": "#/definitions/TimestampProperty"
181
+ },
182
+ {
183
+ "$ref": "#/definitions/JsonProperty"
184
+ },
185
+ {
186
+ "$ref": "#/definitions/EnumProperty"
187
+ },
188
+ {
189
+ "$ref": "#/definitions/EnumRefProperty"
190
+ },
191
+ {
192
+ "$ref": "#/definitions/FileProperty"
193
+ },
194
+ {
195
+ "$ref": "#/definitions/AssociationProperty"
196
+ }
146
197
  ]
147
198
  },
148
199
  "ValidationRules": {
@@ -179,6 +230,10 @@
179
230
  "$ref": "#/definitions/LocalizedString",
180
231
  "description": "Field description/comment"
181
232
  },
233
+ "placeholder": {
234
+ "$ref": "#/definitions/LocalizedString",
235
+ "description": "Placeholder text for form inputs (supports multi-language)"
236
+ },
182
237
  "nullable": {
183
238
  "type": "boolean",
184
239
  "default": false,
@@ -200,19 +255,78 @@
200
255
  "rules": {
201
256
  "$ref": "#/definitions/ValidationRules",
202
257
  "description": "Validation rules (application-level only, NOT database)"
258
+ },
259
+ "hidden": {
260
+ "type": "boolean",
261
+ "default": false,
262
+ "description": "Whether this field should be hidden in API responses/serialization"
263
+ },
264
+ "fillable": {
265
+ "type": "boolean",
266
+ "default": true,
267
+ "description": "Whether this field is mass assignable (Laravel)"
268
+ },
269
+ "fields": {
270
+ "$ref": "#/definitions/CompoundFieldOverrides",
271
+ "description": "Per-field settings for compound types (nullable, hidden, fillable, placeholder overrides)"
272
+ }
273
+ }
274
+ },
275
+ "CompoundFieldOverrides": {
276
+ "type": "object",
277
+ "additionalProperties": {
278
+ "$ref": "#/definitions/CompoundFieldOverride"
279
+ },
280
+ "description": "Per-field overrides for compound types. Keys are field suffixes (e.g., 'Lastname', 'PostalCode')"
281
+ },
282
+ "CompoundFieldOverride": {
283
+ "type": "object",
284
+ "properties": {
285
+ "nullable": {
286
+ "type": "boolean",
287
+ "description": "Override nullable for this field"
288
+ },
289
+ "hidden": {
290
+ "type": "boolean",
291
+ "description": "Override hidden for this field"
292
+ },
293
+ "fillable": {
294
+ "type": "boolean",
295
+ "description": "Override fillable for this field"
296
+ },
297
+ "length": {
298
+ "type": "integer",
299
+ "minimum": 1,
300
+ "description": "Override length for string fields"
301
+ },
302
+ "displayName": {
303
+ "$ref": "#/definitions/LocalizedString",
304
+ "description": "Override display name (label) for this field (supports multi-language)"
305
+ },
306
+ "placeholder": {
307
+ "$ref": "#/definitions/LocalizedString",
308
+ "description": "Override placeholder for this field (supports multi-language)"
203
309
  }
204
310
  }
205
311
  },
206
312
  "StringProperty": {
207
313
  "allOf": [
208
- { "$ref": "#/definitions/BaseProperty" },
314
+ {
315
+ "$ref": "#/definitions/BaseProperty"
316
+ },
209
317
  {
210
318
  "type": "object",
211
- "required": ["type"],
319
+ "required": [
320
+ "type"
321
+ ],
212
322
  "properties": {
213
323
  "type": {
214
324
  "type": "string",
215
- "enum": ["String", "Email", "Password"],
325
+ "enum": [
326
+ "String",
327
+ "Email",
328
+ "Password"
329
+ ],
216
330
  "description": "String type"
217
331
  },
218
332
  "length": {
@@ -228,14 +342,24 @@
228
342
  },
229
343
  "NumericProperty": {
230
344
  "allOf": [
231
- { "$ref": "#/definitions/BaseProperty" },
345
+ {
346
+ "$ref": "#/definitions/BaseProperty"
347
+ },
232
348
  {
233
349
  "type": "object",
234
- "required": ["type"],
350
+ "required": [
351
+ "type"
352
+ ],
235
353
  "properties": {
236
354
  "type": {
237
355
  "type": "string",
238
- "enum": ["TinyInt", "Int", "BigInt", "Float", "Decimal"],
356
+ "enum": [
357
+ "TinyInt",
358
+ "Int",
359
+ "BigInt",
360
+ "Float",
361
+ "Decimal"
362
+ ],
239
363
  "description": "Numeric type - TinyInt (8-bit), Int (32-bit), BigInt (64-bit)"
240
364
  },
241
365
  "unsigned": {
@@ -257,10 +381,14 @@
257
381
  },
258
382
  "BooleanProperty": {
259
383
  "allOf": [
260
- { "$ref": "#/definitions/BaseProperty" },
384
+ {
385
+ "$ref": "#/definitions/BaseProperty"
386
+ },
261
387
  {
262
388
  "type": "object",
263
- "required": ["type"],
389
+ "required": [
390
+ "type"
391
+ ],
264
392
  "properties": {
265
393
  "type": {
266
394
  "const": "Boolean",
@@ -272,14 +400,22 @@
272
400
  },
273
401
  "TextProperty": {
274
402
  "allOf": [
275
- { "$ref": "#/definitions/BaseProperty" },
403
+ {
404
+ "$ref": "#/definitions/BaseProperty"
405
+ },
276
406
  {
277
407
  "type": "object",
278
- "required": ["type"],
408
+ "required": [
409
+ "type"
410
+ ],
279
411
  "properties": {
280
412
  "type": {
281
413
  "type": "string",
282
- "enum": ["Text", "MediumText", "LongText"],
414
+ "enum": [
415
+ "Text",
416
+ "MediumText",
417
+ "LongText"
418
+ ],
283
419
  "description": "Text type - Text (~65KB), MediumText (~16MB), LongText (~4GB)"
284
420
  }
285
421
  }
@@ -288,14 +424,22 @@
288
424
  },
289
425
  "DateTimeProperty": {
290
426
  "allOf": [
291
- { "$ref": "#/definitions/BaseProperty" },
427
+ {
428
+ "$ref": "#/definitions/BaseProperty"
429
+ },
292
430
  {
293
431
  "type": "object",
294
- "required": ["type"],
432
+ "required": [
433
+ "type"
434
+ ],
295
435
  "properties": {
296
436
  "type": {
297
437
  "type": "string",
298
- "enum": ["Date", "DateTime", "Time"],
438
+ "enum": [
439
+ "Date",
440
+ "DateTime",
441
+ "Time"
442
+ ],
299
443
  "description": "Date/Time type"
300
444
  }
301
445
  }
@@ -304,10 +448,14 @@
304
448
  },
305
449
  "TimestampProperty": {
306
450
  "allOf": [
307
- { "$ref": "#/definitions/BaseProperty" },
451
+ {
452
+ "$ref": "#/definitions/BaseProperty"
453
+ },
308
454
  {
309
455
  "type": "object",
310
- "required": ["type"],
456
+ "required": [
457
+ "type"
458
+ ],
311
459
  "properties": {
312
460
  "type": {
313
461
  "const": "Timestamp",
@@ -329,10 +477,14 @@
329
477
  },
330
478
  "JsonProperty": {
331
479
  "allOf": [
332
- { "$ref": "#/definitions/BaseProperty" },
480
+ {
481
+ "$ref": "#/definitions/BaseProperty"
482
+ },
333
483
  {
334
484
  "type": "object",
335
- "required": ["type"],
485
+ "required": [
486
+ "type"
487
+ ],
336
488
  "properties": {
337
489
  "type": {
338
490
  "const": "Json",
@@ -344,10 +496,15 @@
344
496
  },
345
497
  "EnumProperty": {
346
498
  "allOf": [
347
- { "$ref": "#/definitions/BaseProperty" },
499
+ {
500
+ "$ref": "#/definitions/BaseProperty"
501
+ },
348
502
  {
349
503
  "type": "object",
350
- "required": ["type", "enum"],
504
+ "required": [
505
+ "type",
506
+ "enum"
507
+ ],
351
508
  "properties": {
352
509
  "type": {
353
510
  "const": "Enum",
@@ -357,8 +514,12 @@
357
514
  "type": "array",
358
515
  "items": {
359
516
  "oneOf": [
360
- { "type": "string" },
361
- { "$ref": "#/definitions/EnumValue" }
517
+ {
518
+ "type": "string"
519
+ },
520
+ {
521
+ "$ref": "#/definitions/EnumValue"
522
+ }
362
523
  ]
363
524
  },
364
525
  "description": "Inline enum values"
@@ -369,10 +530,15 @@
369
530
  },
370
531
  "EnumRefProperty": {
371
532
  "allOf": [
372
- { "$ref": "#/definitions/BaseProperty" },
533
+ {
534
+ "$ref": "#/definitions/BaseProperty"
535
+ },
373
536
  {
374
537
  "type": "object",
375
- "required": ["type", "enum"],
538
+ "required": [
539
+ "type",
540
+ "enum"
541
+ ],
376
542
  "properties": {
377
543
  "type": {
378
544
  "const": "EnumRef",
@@ -388,7 +554,9 @@
388
554
  },
389
555
  "EnumValue": {
390
556
  "type": "object",
391
- "required": ["value"],
557
+ "required": [
558
+ "value"
559
+ ],
392
560
  "properties": {
393
561
  "value": {
394
562
  "type": "string",
@@ -406,10 +574,14 @@
406
574
  },
407
575
  "FileProperty": {
408
576
  "allOf": [
409
- { "$ref": "#/definitions/BaseProperty" },
577
+ {
578
+ "$ref": "#/definitions/BaseProperty"
579
+ },
410
580
  {
411
581
  "type": "object",
412
- "required": ["type"],
582
+ "required": [
583
+ "type"
584
+ ],
413
585
  "properties": {
414
586
  "type": {
415
587
  "const": "File",
@@ -426,7 +598,9 @@
426
598
  },
427
599
  "accept": {
428
600
  "type": "array",
429
- "items": { "type": "string" },
601
+ "items": {
602
+ "type": "string"
603
+ },
430
604
  "description": "Accepted file extensions (e.g., ['jpg', 'png', 'pdf'])"
431
605
  },
432
606
  "maxSize": {
@@ -439,7 +613,10 @@
439
613
  },
440
614
  "AssociationProperty": {
441
615
  "type": "object",
442
- "required": ["type", "relation"],
616
+ "required": [
617
+ "type",
618
+ "relation"
619
+ ],
443
620
  "properties": {
444
621
  "type": {
445
622
  "const": "Association",
@@ -470,12 +647,24 @@
470
647
  },
471
648
  "onDelete": {
472
649
  "type": "string",
473
- "enum": ["CASCADE", "SET NULL", "SET DEFAULT", "RESTRICT", "NO ACTION"],
650
+ "enum": [
651
+ "CASCADE",
652
+ "SET NULL",
653
+ "SET DEFAULT",
654
+ "RESTRICT",
655
+ "NO ACTION"
656
+ ],
474
657
  "description": "Action when referenced record is deleted"
475
658
  },
476
659
  "onUpdate": {
477
660
  "type": "string",
478
- "enum": ["CASCADE", "SET NULL", "SET DEFAULT", "RESTRICT", "NO ACTION"],
661
+ "enum": [
662
+ "CASCADE",
663
+ "SET NULL",
664
+ "SET DEFAULT",
665
+ "RESTRICT",
666
+ "NO ACTION"
667
+ ],
479
668
  "description": "Action when referenced record is updated"
480
669
  },
481
670
  "joinTable": {
@@ -492,7 +681,9 @@
492
681
  },
493
682
  "targets": {
494
683
  "type": "array",
495
- "items": { "type": "string" },
684
+ "items": {
685
+ "type": "string"
686
+ },
496
687
  "description": "Target schema names for MorphTo (e.g., ['Post', 'Video'])"
497
688
  },
498
689
  "morphName": {
@@ -521,12 +712,18 @@
521
712
  "properties": {
522
713
  "name": {
523
714
  "type": "String",
524
- "displayName": { "ja": "氏名", "en": "Full Name" }
715
+ "displayName": {
716
+ "ja": "氏名",
717
+ "en": "Full Name"
718
+ }
525
719
  },
526
720
  "email": {
527
721
  "type": "Email",
528
722
  "unique": true,
529
- "displayName": { "ja": "メールアドレス", "en": "Email" }
723
+ "displayName": {
724
+ "ja": "メールアドレス",
725
+ "en": "Email"
726
+ }
530
727
  },
531
728
  "password": {
532
729
  "type": "Password"
@@ -547,18 +744,33 @@
547
744
  "options": {
548
745
  "softDelete": true,
549
746
  "indexes": [
550
- { "columns": ["published_at"] },
551
- { "columns": ["status", "published_at"] }
747
+ {
748
+ "columns": [
749
+ "published_at"
750
+ ]
751
+ },
752
+ {
753
+ "columns": [
754
+ "status",
755
+ "published_at"
756
+ ]
757
+ }
552
758
  ]
553
759
  },
554
760
  "properties": {
555
761
  "title": {
556
762
  "type": "String",
557
- "displayName": { "ja": "タイトル", "en": "Title" }
763
+ "displayName": {
764
+ "ja": "タイトル",
765
+ "en": "Title"
766
+ }
558
767
  },
559
768
  "content": {
560
769
  "type": "LongText",
561
- "displayName": { "ja": "本文", "en": "Content" }
770
+ "displayName": {
771
+ "ja": "本文",
772
+ "en": "Content"
773
+ }
562
774
  },
563
775
  "status": {
564
776
  "type": "EnumRef",
@@ -581,9 +793,27 @@
581
793
  "en": "Post Status"
582
794
  },
583
795
  "values": [
584
- { "value": "draft", "label": { "ja": "下書き", "en": "Draft" } },
585
- { "value": "published", "label": { "ja": "公開済み", "en": "Published" } },
586
- { "value": "archived", "label": { "ja": "アーカイブ", "en": "Archived" } }
796
+ {
797
+ "value": "draft",
798
+ "label": {
799
+ "ja": "下書き",
800
+ "en": "Draft"
801
+ }
802
+ },
803
+ {
804
+ "value": "published",
805
+ "label": {
806
+ "ja": "公開済み",
807
+ "en": "Published"
808
+ }
809
+ },
810
+ {
811
+ "value": "archived",
812
+ "label": {
813
+ "ja": "アーカイブ",
814
+ "en": "Archived"
815
+ }
816
+ }
587
817
  ]
588
818
  },
589
819
  {
@@ -600,17 +830,26 @@
600
830
  "key": {
601
831
  "type": "String",
602
832
  "primary": true,
603
- "displayName": { "ja": "キー", "en": "Key" }
833
+ "displayName": {
834
+ "ja": "キー",
835
+ "en": "Key"
836
+ }
604
837
  },
605
838
  "value": {
606
839
  "type": "Text",
607
- "displayName": { "ja": "値", "en": "Value" }
840
+ "displayName": {
841
+ "ja": "値",
842
+ "en": "Value"
843
+ }
608
844
  },
609
845
  "expiration": {
610
846
  "type": "Int",
611
- "displayName": { "ja": "有効期限", "en": "Expiration" }
847
+ "displayName": {
848
+ "ja": "有効期限",
849
+ "en": "Expiration"
850
+ }
612
851
  }
613
852
  }
614
853
  }
615
854
  ]
616
- }
855
+ }