/** * @omnify/ts — Internal types * * Types for schemas.json input and TypeScript code generation output. */ /** Localized string — either a plain string or a locale map. */ export type LocalizedString = string | Record; /** File upload configuration. */ export interface FileConfigExport { readonly tempFlow?: boolean; readonly tempTtl?: string; readonly cleanupSchedule?: string; readonly purgeAfter?: string; readonly purgeSchedule?: string; readonly defaultDisk?: string; } /** * Global audit configuration surfaced from the Go core. * * `model` is the FQ schema name of the user / actor (typically `User`) * — the audits table records `(user_type, user_id)` polymorphically so * any model can be the actor, but the trait needs a default to capture * `Auth::user()` correctly. * * `log` toggles the `audits` history table feature globally; per-schema * `options.audit.log` overrides per-schema. Sensitive columns listed in * `logExclude` are scrubbed from `old_values` / `new_values` BEFORE the * row is written, so a stolen audits dump cannot leak passwords. * * `logRetention` is the Prunable retention period (`90d`, `12w`, `6m`, * `1y`); empty means audits are kept forever (no scheduled prune). * * `logQueue` is the Laravel queue connection name. Empty string means * sync dispatch (low-traffic / dev fallback) — audited writes still * complete immediately, just on the request thread. */ export interface AuditConfigExport { readonly model?: string; readonly createdBy?: boolean; readonly updatedBy?: boolean; readonly deletedBy?: boolean; readonly log?: boolean; readonly logExclude?: readonly string[]; readonly logRetention?: string; readonly logQueue?: string; } /** Top-level schemas.json structure. */ export interface SchemasJson { /** * Optional. Removed from omnify-go output in #52 to make schemas.json * byte-deterministic so backends can commit it as a build artifact. * Kept here as optional for backwards compat with previously committed * schemas.json files that still contain the field. */ readonly generatedAt?: string; readonly database: { readonly driver: string; }; readonly connections: Record; readonly locale: { readonly locales: string[]; readonly defaultLocale: string; readonly fallbackLocale: string; readonly enforceLanguageFk?: boolean; }; readonly customTypes: { readonly compound: Record; readonly simple: Record; readonly enums: Record; }; readonly fileConfig?: FileConfigExport; readonly auditConfig?: AuditConfigExport; readonly packages?: Record; readonly schemas: Record; } /** Package metadata in schemas.json (codegen namespace references). */ export interface PackageExportInfo { readonly migrationsPath?: string; readonly codegen?: PackageCodegenExport; /** Schema name → consumer FQCN mapping for relation resolution. Issue #61. */ readonly modelMap?: Record; /** Whether this package's models are MappedSuperclass (abstract). Issue #61. */ readonly abstract?: boolean; } /** Codegen namespace info from a package. */ export interface PackageCodegenExport { readonly laravel?: { readonly model?: { readonly namespace?: string; }; readonly request?: { readonly namespace?: string; }; readonly resource?: { readonly namespace?: string; }; readonly factory?: { readonly namespace?: string; }; }; readonly typescript?: { readonly modelsPath?: string; }; } /** Compound type definition (e.g., JapaneseName). */ export interface CompoundTypeDefinition { readonly fields: readonly CompoundField[]; } /** A field within a compound type. */ export interface CompoundField { readonly suffix: string; readonly mapsTo?: string; readonly length?: number; readonly nullable?: boolean; readonly enumRef?: string; } /** Simple custom type (e.g., JapanesePhone). */ export interface SimpleTypeDefinition { readonly mapsTo: string; readonly length?: number; } /** Expanded property info (pre-computed by omnify-go). */ export interface ExpandedProperty { readonly sourceType: string; readonly columns: readonly ExpandedColumn[]; } /** A single expanded column from a compound type. */ export interface ExpandedColumn { readonly name: string; readonly suffix: string; readonly type: string; readonly length?: number; readonly nullable?: boolean; readonly enum?: readonly string[]; } /** API generation options for a schema. */ export interface ApiOptions { readonly prefix?: string; readonly actions?: readonly string[]; readonly lookup?: boolean; readonly bulkDelete?: boolean; readonly restore?: boolean; readonly middleware?: readonly string[]; readonly perPage?: number; } /** * Service layer codegen options — controls BaseService generation. Issue #57. * * #81 (v3.23.0): every field in this interface is DEPRECATED. Use the * property-level flags instead (searchable / filterable / sortable / * lookupable / defaultSort), which are the single source of truth. This * interface is kept for backward compatibility and emits deprecation * warnings from the generator when any legacy key is set. * * The only keys that will survive the deprecation cycle are legitimate * service-level overrides that property-level can't express (pagination * strategy, per_page default, etc.) — those will be added in a future * release when the deprecations clear. */ export interface ServiceOptions { /** @deprecated #81 — use property-level `searchable: true` instead */ readonly searchable?: readonly string[]; /** @deprecated #81 — use property-level `filterable: true` instead */ readonly filterable?: readonly string[]; /** @deprecated #81 — use property-level `defaultSort: 'asc'|'desc'` on exactly one field */ readonly defaultSort?: string; /** @deprecated #81 — override applyListEagerLoads/applyFindByIdEagerLoads/applyLookupEagerLoads in the editable service instead */ readonly eagerLoad?: readonly string[]; /** @deprecated #81 — override applyListEagerLoads hook in the editable service instead */ readonly eagerCount?: readonly string[]; /** @deprecated #81 — use property-level `lookupable: true` instead */ readonly lookupFields?: readonly string[]; } /** Schema options. */ export interface SchemaOptions { readonly id?: boolean | string; readonly timestamps?: boolean | { createdAt?: boolean; updatedAt?: boolean; created_at?: boolean; updated_at?: boolean; }; readonly softDelete?: boolean; readonly hidden?: boolean; readonly nestedSet?: boolean; readonly tableName?: string; readonly indexes?: readonly unknown[]; readonly unique?: readonly unknown[]; readonly api?: ApiOptions; readonly audit?: { readonly model?: string; readonly createdBy?: boolean; readonly updatedBy?: boolean; readonly deletedBy?: boolean; /** Per-schema opt-in to the audits history feature. Pointer-tristate * semantics: undefined = inherit global; true/false = explicit. */ readonly log?: boolean; /** Per-schema scrub list. Merged with the global `auditConfig.logExclude` * before any audit row is written. */ readonly logExclude?: readonly string[]; /** Static tag list applied to every audit row from this schema. */ readonly logTags?: readonly string[]; }; /** * Service layer codegen options (issue #57). * * #81 (v3.23.0): accepts `false` as an explicit opt-out — every `kind: * object` project schema gets a generated base service by default now. * Set `service: false` on pivot tables / sidecars / audit logs that * shouldn't have a service. Set to an object only for legitimate * service-level overrides that property-level flags can't express * (legacy property-duplication keys emit deprecation warnings at * generate time). */ readonly service?: ServiceOptions | false; /** * Per-schema opt-out for the auto-generated Policy. Issue #98 v5.8.14: * every `kind: object` schema gets a generated policy base + editable * stub by default. Set `policy: false` on schemas that should NOT have * an authorization policy (translation tables, internal sidecars, etc). * Cedar-style ABAC `policies:` array on the root schema definition * still drives the generated method bodies when present; otherwise the * generator emits a standard 5-method CRUD scaffold with * `return true;` bodies for the project to override in the editable. */ readonly policy?: false; /** Schema-level default ordering — generates a global Eloquent scope. Issue #40. */ readonly defaultOrder?: readonly OrderByItem[]; } /** Schema definition from schemas.json. */ export interface SchemaDefinition { readonly name: string; readonly package?: string | null; readonly tableName?: string; readonly connection?: string; readonly displayName?: LocalizedString; readonly group?: string; readonly kind?: 'object' | 'enum' | 'pivot' | 'partial' | 'extend'; readonly options?: SchemaOptions; readonly properties?: Record; readonly propertyOrder?: readonly string[]; readonly expandedProperties?: Record; /** * Properties a project-level `kind: extend` merged into this schema, in * merge order. The Go loader folds an extend into its target and deletes * the extend, so this is the only remaining trace of where they came from * — which a PACKAGE-owned target needs, because the package's own generate * run never saw them (#164). */ readonly extendedProperties?: readonly string[]; readonly values?: readonly EnumValueDefinition[]; readonly pivotFor?: readonly string[]; readonly policies?: readonly PolicyDefinition[]; } /** Operand type in a policy condition. */ export type OperandType = 'property' | 'variable' | 'literal' | 'cidr' | 'set' | 'path'; /** One side of a condition expression. */ export interface ConditionOperand { readonly type: OperandType; readonly name?: string; readonly value?: string | number | boolean; readonly items?: readonly string[]; } /** A parsed binary condition expression. */ export interface SingleCondition { readonly operator: string; readonly left: ConditionOperand; readonly right: ConditionOperand; } /** AND group of conditions. */ export interface PolicyConditionAnd { readonly operator: 'and'; readonly conditions: readonly SingleCondition[]; } /** Policy when clause — either a single condition or AND group. */ export type PolicyWhen = SingleCondition | PolicyConditionAnd; /** A single policy rule (permit or forbid). */ export interface PolicyDefinition { readonly effect: 'permit' | 'forbid'; readonly actions: readonly string[]; readonly when?: PolicyWhen; readonly desc?: string; } /** Application-level validation rules. Single source of truth for both Laravel validation and Zod schemas. */ export interface ValidationRules { readonly required?: boolean; readonly minLength?: number; readonly maxLength?: number; readonly url?: boolean; readonly uuid?: boolean; readonly ip?: boolean; readonly ipv4?: boolean; readonly ipv6?: boolean; readonly alpha?: boolean; readonly alphaNum?: boolean; readonly alphaDash?: boolean; readonly numeric?: boolean; readonly digits?: number; readonly digitsBetween?: readonly [number, number]; readonly startsWith?: string | readonly string[]; readonly endsWith?: string | readonly string[]; readonly lowercase?: boolean; readonly uppercase?: boolean; readonly min?: number; readonly max?: number; readonly between?: readonly [number, number]; readonly gt?: number; readonly lt?: number; readonly multipleOf?: number; readonly arrayMin?: number; readonly arrayMax?: number; } /** Property definition within a schema. */ export interface PropertyDefinition { readonly type: string; readonly displayName?: LocalizedString; readonly description?: LocalizedString; readonly placeholder?: LocalizedString; readonly nullable?: boolean; readonly unique?: boolean; readonly primary?: boolean; readonly translatable?: boolean; /** Database-generated stored column expression. Generated columns are read-only. */ readonly storedAs?: string; readonly default?: unknown; readonly length?: number; readonly minLength?: number; readonly maxLength?: number; readonly min?: number; readonly max?: number; readonly unsigned?: boolean; /** * AUTO_INCREMENT on this column (#175). TinyInt/Int/BigInt only, and only on * the table's single-column primary key. Read by the model generator: an * integer primary key WITHOUT it gets `$incrementing = false`. */ readonly autoIncrement?: boolean; /** * Target primary key type for a relation's FK column. Honoured on MorphTo as * the explicit override for the `_id` column's type (#176). */ readonly idType?: string; readonly precision?: number; readonly scale?: number; readonly pattern?: string; readonly rules?: ValidationRules; readonly enum?: string | readonly string[]; readonly multiple?: boolean; readonly maxFiles?: number; readonly accept?: readonly string[]; readonly maxSize?: number; readonly collection?: string; readonly relation?: string; readonly target?: string; readonly targets?: readonly string[]; readonly onDelete?: string; readonly morphName?: string; readonly joinTable?: string; readonly mappedBy?: string; /** Physical FK column override for owning ManyToOne/OneToOne associations. */ readonly column?: string; readonly orderBy?: readonly OrderByItem[]; readonly useCurrent?: boolean; readonly deprecated?: boolean; readonly deprecatedSince?: string; readonly removalTarget?: string; readonly searchable?: boolean; readonly filterable?: boolean; readonly sortable?: boolean; /** * #81: mark a property as part of the lookup() projection (id/label/slug * shape for type-ahead / dropdown endpoints). When at least one property * on a schema has `lookupable: true`, the generator uses that set instead * of the legacy `options.service.lookupFields` array. */ readonly lookupable?: boolean; /** * #81: declare this property as the schema's default sort column. At most * one property per schema should set this. The value is the direction: * `asc` emits `defaultSort: `, `desc` emits `-`. Legacy * `options.service.defaultSort` is still honoured with a deprecation * warning. */ readonly defaultSort?: 'asc' | 'desc'; readonly fields?: Record; } /** Single column ordering. Direction defaults to 'asc'. Issue #40. */ export interface OrderByItem { readonly column: string; readonly direction?: string; } /** Field override for compound type properties. */ export interface FieldOverride { readonly nullable?: boolean; readonly length?: number; readonly hidden?: boolean; readonly displayName?: LocalizedString; readonly placeholder?: LocalizedString; } /** Enum value definition (for schema enums). */ export interface EnumValueDefinition { readonly value: string; readonly label?: LocalizedString; readonly extra?: Record; } /** File category for organizing output. */ export type FileCategory = 'schema' | 'base' | 'enum' | 'plugin-enum'; /** Generated TypeScript file. */ export interface TypeScriptFile { readonly filePath: string; readonly content: string; readonly types: readonly string[]; readonly overwrite: boolean; readonly category?: FileCategory; } /** TypeScript property definition. */ export interface TSProperty { readonly name: string; readonly type: string; readonly optional: boolean; readonly readonly: boolean; readonly comment?: string; /** * The underlying schema column is `nullable: true` (DB allows NULL), * NOT just "optional in some payload shape". When set, formatProperty * emits `field?: T | null` so consumers can accept the actual JSON * value the backend serializes (`null`, not `undefined`). Mirrors the * Go target's `*T` pointer convention. Phase 2 of issue #103. */ readonly nullable?: boolean; } /** TypeScript interface definition. */ export interface TSInterface { readonly name: string; readonly properties: readonly TSProperty[]; readonly extends?: readonly string[]; readonly comment?: string; readonly dependencies?: readonly string[]; readonly enumDependencies?: readonly string[]; } /** TypeScript enum definition. */ export interface TSEnum { readonly name: string; readonly values: readonly TSEnumValue[]; readonly comment?: string; } /** Multi-locale string map. */ export type LocaleMap = Record; /** TypeScript enum value. */ export interface TSEnumValue { readonly name: string; readonly value: string | number; readonly label?: string | LocaleMap; readonly extra?: Record; } /** TypeScript type alias definition. */ export interface TSTypeAlias { readonly name: string; readonly type: string; readonly comment?: string; } /** Zod schema information for a property. */ export interface ZodPropertySchema { readonly fieldName: string; readonly schema: string; readonly inCreate: boolean; readonly inUpdate: boolean; readonly comment?: string; } /** Display names for a schema. */ export interface SchemaDisplayNames { readonly displayName: LocaleMap; readonly propertyDisplayNames: Record; readonly propertyPlaceholders: Record; readonly propertyDescriptions: Record; } /** Generation options (internal). */ export interface GeneratorOptions { readonly locales: string[]; readonly defaultLocale: string; readonly fallbackLocale: string; readonly customTypes: SchemasJson['customTypes']; /** Target platform: 'web' (default) or 'expo' (React Native). Issue #63. */ readonly platform?: 'web' | 'expo'; /** Auth strategy: 'cookie' (default) or 'secureStore' (Expo). Issue #63. */ readonly auth?: 'cookie' | 'secureStore'; /** * Enum emission style. Issue #103 Issue 1. * * - `'enum'` (default): emit `export enum X { ... }` — TypeScript native * syntax. Compatible with projects predating TS 5.5 / projects that * declaration-merge enums via namespace. INCOMPATIBLE with the * `erasableSyntaxOnly` strict-mode option (TS 5.5+ default in Vite 7 * starters), which rejects enums because they emit runtime code that * type-only-stripping tools (esbuild/deno/bun) can't erase. * * - `'const'`: emit `export const X = {...} as const; export type X = ...` * — works under all strict modes. Identical call-site ergonomics * (`X.User === 'user'`, type narrowing, helper functions). Pilot * downstream uses this when their tsconfig enables erasableSyntaxOnly. * * Default `'enum'` preserves backwards compatibility for projects * upgrading omnify without changing their omnify.yaml — set * `enumStyle: 'const'` explicitly to opt into the strict-mode-friendly * output. */ readonly enumStyle?: 'enum' | 'const'; }