/** * Types for PHP code generation output. */ /** A generated PHP file. */ export interface GeneratedFile { readonly path: string; readonly content: string; readonly overwrite: boolean; readonly type: 'base' | 'user'; /** * For a user file only: something the file has to contain that a version * written by an EARLIER omnify would not have. * * A user file is created once and never rewritten, so a template that grows a * new line reaches new projects and silently misses every existing one. The * writer checks this against the file it skipped and prints `hint` when it is * absent — telling the one project that needs the edit, rather than warning * everybody on every run or overwriting somebody's code to add a line. */ readonly expects?: { readonly needle: string; readonly hint: string; }; /** * For a user file only: things the file must NOT contain, and why. * * The mirror of `expects`. Some edits to a user-editable subclass silently * disable what the base class generates — the worst being a redeclared * `protected $fillable`, which in PHP REPLACES the parent's rather than * merging, so every property added to the schema afterwards is stripped by * Eloquent on mass assignment. Nothing throws; `save()` returns true and the * column stays NULL (issue #171). * * Each entry is matched against the content of the file we skipped, and * `hint` is printed when it matches. */ readonly conflicts?: readonly { readonly pattern: string; readonly hint: string; }[]; } /** Create a base file (always overwritten). */ export declare function baseFile(path: string, content: string): GeneratedFile; /** Create a user file (created once, skip if exists). */ export declare function userFile(path: string, content: string, expects?: { needle: string; hint: string; }, conflicts?: readonly { pattern: string; hint: string; }[]): GeneratedFile; /** * Eloquent array properties that REPLACE the parent's when redeclared in a * subclass, rather than merging. Redeclaring any of these in a user-editable * model shadows whatever the generated base puts there, and the failure is * completely silent. Issue #171. */ export declare const SHADOWING_ELOQUENT_PROPERTIES: readonly ["fillable", "guarded", "casts", "hidden", "visible", "appends", "dates", "touches", "with"]; /** * Build the `conflicts` entries for a generated model's editable subclass. * * `baseClass` is named in the hint because the whole point is that the base is * still correct — the generator did emit the property — and the reader needs to * know which file they are shadowing. */ export declare function shadowingPropertyConflicts(baseClass: string): { pattern: string; hint: string; }[]; /** Category of base file for modular path resolution. */ export type BaseCategory = 'Models' | 'Controllers' | 'Requests' | 'Resources' | 'Services' | 'Enums' | 'Traits' | 'Locales' | 'Policies'; /** * Resolve base file path for a schema based on structure. * Modular: {modules.path}/{SchemaName}/{category}/{fileName} * Legacy: {legacyPath}/{fileName} */ export declare function resolveModularBasePath(config: PhpConfig, schemaName: string, category: BaseCategory, fileName: string, legacyPath: string): string; /** * Resolve base namespace for a schema based on structure. * Modular: {modules.namespace}\{SchemaName}\{Category} * Legacy: {legacyNamespace} */ export declare function resolveModularBaseNamespace(config: PhpConfig, schemaName: string, category: BaseCategory, legacyNamespace: string): string; /** * Resolve shared base path (for cross-module files like BaseModel). * Modular: {shared.path}/{category}/{fileName} * Legacy: {legacyPath}/{fileName} */ export declare function resolveSharedBasePath(config: PhpConfig, category: BaseCategory, fileName: string, legacyPath: string): string; /** * Resolve shared base namespace. * Modular: {shared.namespace}\{Category} * Legacy: {legacyNamespace} */ export declare function resolveSharedBaseNamespace(config: PhpConfig, category: BaseCategory, legacyNamespace: string): string; /** * Resolve global Omnify enum file path. ALWAYS uses * `config.globalEnums.path` regardless of structure mode — enums are * conceptually independent of the model tree they describe and belong * in their own dedicated folder for discoverability. The `legacyPath` * argument is kept for callsite compatibility but ignored. * * Default: `app/Omnify/Enums/`. Override via `codegen.laravel.enums.path` * in `omnify.yaml` (e.g. set to `app/Models/Omnify` to restore the * pre-v5.6 legacy behavior of co-locating enums with models). * * History: pre-v5.6, legacy structure dumped enums into the model dir * (`app/Models/Omnify/Enum.php`), mixing them with the user * models — confusing for code reviewers and inconsistent with modular * mode. Fixed via dedicated dir always. */ export declare function resolveGlobalEnumPath(config: PhpConfig, fileName: string, _legacyPath: string): string; /** * Resolve global Omnify enum namespace. ALWAYS uses * `config.globalEnums.namespace`. The `legacyNamespace` argument is * kept for callsite compatibility but ignored. * * Default: `App\Omnify\Enums`. Override via * `codegen.laravel.enums.namespace` to match a custom path. * * History: pre-v5.6, legacy structure used the model namespace * (`App\Models\Omnify`), forcing every model file to import enums via * the model dir. Fixed via dedicated namespace always. */ export declare function resolveGlobalEnumNamespace(config: PhpConfig, _legacyNamespace: string): string; /** Resolve global Omnify trait file path (HasFiles, HasLocalizedDisplayName, ...). */ export declare function resolveGlobalTraitPath(config: PhpConfig, fileName: string, legacyPath: string): string; /** Resolve global Omnify trait namespace. */ export declare function resolveGlobalTraitNamespace(config: PhpConfig, legacyNamespace: string): string; /** * Per-target path and namespace override from codegen.laravel config. * * Two evolutions in v5.4+ (issue #96): split base / user-editable paths * and `flatBase` to drop the `Base/` subfolder + `*Base*` suffix when * the team has already isolated generated code under its own root * (e.g. `app/Omnify/`). * * Backwards-compat: if `userEditablePath` / `userEditableNamespace` / * `flatBase` are absent, the existing two-tier layout (base + editable * in the same dir, base under `Base/` subfolder with `*Base*` suffix) * stays exactly as before. No project needs to change config to keep * working. */ export interface LaravelPathOverride { /** Disable generation for layers that support it (currently `service`). */ enable?: boolean; /** Path for BASE (auto-generated, regenerated) classes. */ path?: string; /** Namespace for BASE (auto-generated) classes. */ namespace?: string; /** * Path for USER-EDITABLE stubs (omnify writes once, then never touches). * When set, base files write under `path` and editable stubs under * `userEditablePath`. When unset, editable stubs share `path` (legacy * behavior). Issue #96. */ userEditablePath?: string; /** * Namespace for USER-EDITABLE stubs. When set, the editable stub uses * this namespace and `extends \{baseNs}\{Class}`. When unset, the * editable stub shares the base namespace (legacy). Issue #96. */ userEditableNamespace?: string; /** * `flatBase: true` drops the `Base/` (or `OmnifyBase/`) subfolder AND * the `*BaseModel` / `*RequestBase` / etc. class suffix. Base files * land directly under `path` with the bare schema name as the class. * The user-editable stub then `extends \{path-namespace}\{Schema}` — * one less indirection. Issue #96. Recommended when `path` already * isolates generated code (e.g. `app/Omnify/Models/`). */ flatBase?: boolean; /** * Issue #98 v5.8.5: per-layer toggle for whether the USER-EDITABLE * stub is grouped by the schema's parent folder. The base layer is * always grouped (canonical layout — domain-organized regenerated * code), but Laravel ecosystem code (`app/Models/`, `app/Services/`, * `app/Http/Requests/`, `app/Http/Resources/`) is canonically FLAT — * `App\Models\User`, not `App\Models\Auth\User`. `config/auth.php`, * Sanctum traits, factory discovery and dozens of ecosystem packages * assume the flat shape. * * Default: `false` (Laravel-canonical flat editable). The grouped * base + flat editable layout is what new `composer create-project * laravel/laravel` apps produce, and the use-statement in the editable * stub still aliases the GROUPED base FQN — group is encoded in the * `use ... as ...Base` import, not the editable's namespace. * * Projects that adopted v5.8.x and want to keep their grouped editable * layout flip this to `true` per layer. */ userEditableGroupByFolder?: boolean; } /** Nested set package configuration. */ export interface NestedSetOverride { namespace?: string; } /** * OpenAPI / Swagger annotation generation override (issue #35). * Opt-in: only emits files / attributes when `enable: true`. * * Generates `OpenApi/Common.php`, `OpenApi/OmnifyApiInfo.php`, and one * `OpenApi/Schemas/{Name}Schema.php` per schema with `options.api`. When * enabled, also injects `#[OA\Get/Post/...]` attributes onto the auto-generated * controller CRUD methods so docs stay in sync with the actual handlers. * * Requires `darkaonline/l5-swagger` (or any zircote/swagger-php consumer) * installed in the target Laravel project. */ export interface OpenApiOverride { /** Toggle OpenAPI codegen on/off. Off by default — opt-in feature. */ enable?: boolean; /** PHP namespace for the generated `OpenApi/...` classes. Default `App\Omnify\OpenApi`. */ namespace?: string; /** Filesystem path for the generated `OpenApi/...` files. Default `app/Omnify/OpenApi`. */ path?: string; /** URL prefix prepended to every generated path attribute. Default `/api/v1`. */ pathsPrefix?: string; /** Optional prefix for tag names (e.g. `Brand` → `BrandProducts`). */ tagsPrefix?: string; /** Name of the OpenAPI security scheme to attach to every endpoint. Default `sanctum`. */ securityScheme?: string; } /** Overrides from codegen.laravel YAML config (all optional). */ export interface LaravelCodegenOverrides { /** * Filesystem prefix applied to ALL generated paths (defaults + explicit overrides * that are not already absolute). Use this for monorepo setups where the Laravel * project lives in a subdirectory, e.g. `rootPath: backend` makes everything * write under `backend/app/...`, `backend/config/...`, etc. */ rootPath?: string; structure?: 'legacy' | 'modular'; model?: LaravelPathOverride; request?: LaravelPathOverride; resource?: LaravelPathOverride; factory?: LaravelPathOverride; provider?: LaravelPathOverride; policy?: LaravelPathOverride; controller?: LaravelPathOverride; service?: LaravelPathOverride; /** * Per-schema base-class root for `structure: modular`. Default * `App\Omnify\Modules` (path `app/Omnify/Modules`) so generated base classes * never collide with a team's hand-written `app/Modules/` (Modular Monolith * pattern is common in Laravel). */ modules?: LaravelPathOverride; /** Cross-module shared classes (e.g. `BaseModel`). Default `App\Omnify\Shared`. */ shared?: LaravelPathOverride; /** * Global Omnify enums that aren't tied to a single user-defined schema * (e.g. `FileStatusEnum` for the file-attachment subsystem). * Default `App\Omnify\Enums`. */ enums?: LaravelPathOverride; /** * Global Omnify traits (e.g. `HasFiles`, `HasLocalizedDisplayName`). * Default `App\Omnify\Traits`. */ traits?: LaravelPathOverride; route?: LaravelPathOverride; config?: LaravelPathOverride; nestedset?: NestedSetOverride; /** OpenAPI / Swagger codegen — opt-in. See `OpenApiOverride`. */ openapi?: OpenApiOverride; } /** * Resolved per-layer config for layers that follow the Base + Editable * pattern (Model, Service, Request, Resource, Policy, Controller). * Issue #96 added `userEditablePath` / `userEditableNamespace` (split * paths) and `flatBase` (drop Base/ subfolder + *Base* suffix). * * Backwards-compat: when the new fields default, `userEditablePath` * equals `path` and `userEditableNamespace` equals `namespace` — * editable stubs share the same dir as the base, identical to v5.3.x. * `flatBase` defaults false — keeps the existing `*BaseModel` / * `*RequestBase` / etc. suffix + Base/ subfolder. */ export interface BaseEditableLayer { /** Namespace for base classes. */ namespace: string; /** Namespace for base classes' Base/ subfolder (or = namespace when flatBase). */ baseNamespace: string; /** Filesystem path where base classes live. */ path: string; /** Filesystem path for the Base/ subfolder (or = path when flatBase). */ basePath: string; /** * Filesystem path for user-editable stubs. Defaults to `path` (legacy: * stubs share the dir with base). Set via `userEditablePath` override * to relocate the editable stubs to e.g. canonical Laravel paths * (`app/Models/`, `app/Http/Requests/`). Issue #96. */ userEditablePath: string; /** Namespace for user-editable stubs. Defaults to `namespace`. Issue #96. */ userEditableNamespace: string; /** * `true` collapses the legacy two-tier shape (base under `Base/` with * `*Base*` suffix) into a single flat directory: base lives at `path` * with the bare schema name as the class. The editable stub then * extends `\{namespace}\{Schema}` (no `BaseModel` indirection). Issue #96. */ flatBase: boolean; /** * Issue #98 v5.8.5: when `true`, the user-editable layer is also * grouped by the schema's parent folder (mirrors the base layer). * Default `false` — Laravel-canonical flat layout (`app/Models/User.php`, * not `app/Models/Auth/User.php`). Group is encoded only in the base * layer + the `use ... as Base` import inside the editable stub. * The base layer is unaffected — it is always grouped. */ userEditableGroupByFolder: boolean; } /** PHP codegen configuration (resolved with defaults). */ export interface PhpConfig { /** Filesystem prefix applied to all generated paths. Empty when not set. */ rootPath: string; structure: 'legacy' | 'modular'; /** * Modular base-class root: `{path}/{Schema}/{Layer}/...` and * `{namespace}\{Schema}\{Layer}`. Only consulted in `structure: modular`. */ modules: { namespace: string; path: string; }; /** Cross-module shared root (e.g. BaseModel lives at `{shared.path}/Models/`). */ shared: { namespace: string; path: string; }; /** Global Omnify enums (e.g. FileStatusEnum). */ globalEnums: { namespace: string; path: string; }; /** Global Omnify traits (e.g. HasFiles, HasLocalizedDisplayName). */ globalTraits: { namespace: string; path: string; }; /** * Backward-compat alias for `modules.path`. Kept so external callers and * tests that read `config.modulesPath` keep working; new code should use * `config.modules.path` instead. * @deprecated use `modules.path` */ modulesPath: string; models: BaseEditableLayer; requests: BaseEditableLayer; resources: BaseEditableLayer; factories: { namespace: string; path: string; }; providers: { namespace: string; path: string; }; policies: BaseEditableLayer; controllers: BaseEditableLayer; services: BaseEditableLayer & { /** False disables Laravel service emission and cleanup for this target. */ enabled: boolean; }; routes: { path: string; }; /** Path to the omnify-schemas.php config file (full path including filename). */ configFile: { path: string; }; nestedset: { namespace: string; }; /** OpenAPI / Swagger codegen config (issue #35). */ openapi: { enable: boolean; namespace: string; path: string; pathsPrefix: string; tagsPrefix: string; securityScheme: string; }; } /** * Apply group-by-folder nesting (issue #98 v5.8+). When the schema's * `group:` field is non-empty, append `` as a subfolder * AND a sub-namespace. Schemas without `group:` are unchanged (no * synthetic `Default/` / `Misc/` folder per the issue thread). * * Used by every generator's base + editable path resolution so files * mirror the schema directory layout. With 100+ schemas across many * groups, the flat output is unusable; nesting matches what devs * already expect since the schemas themselves live at * `schemas//.yaml`. * * Only applies to LEGACY structure; modular structure already nests * per-Schema (`app/Omnify/Modules///`) so adding group * on top would be redundant. */ export declare function nestByGroup(base: { path: string; namespace: string; }, group: string | undefined): { path: string; namespace: string; }; /** * Class-name + file-name resolver for the BASE class of a layer. Returns * the bare schema name when `layer.flatBase` is true (e.g. `Banner.php` * with class `Banner`); returns the legacy `` shape * otherwise (e.g. `BannerBaseModel.php` with class `BannerBaseModel`). * * Used by every Base + Editable generator (model, service, request, * resource, policy, controller) so the flatBase semantics stay * consistent across layers. Issue #96. */ export declare function resolveBaseClass(layer: BaseEditableLayer, schemaName: string, baseSuffix: string): { className: string; fileName: string; namespace: string; path: string; fqn: string; }; /** * Resolver for the user-editable stub of a layer. Always uses the bare * schema name as the class (the editable stub IS the user's class) + * `userEditablePath` / `userEditableNamespace` for location. Issue #96. */ export declare function resolveEditableClass(layer: BaseEditableLayer, schemaName: string, classSuffix?: string): { className: string; fileName: string; namespace: string; path: string; fqn: string; }; /** * Issue #98 v5.8.5: gate group nesting for the user-editable layer on * `layer.userEditableGroupByFolder`. Default flat (Laravel-canonical) — * `app/Models/User.php`, `App\Models\User`. Opt-in to mirror-base via * `userEditableGroupByFolder: true` per layer. Auxiliary subfolders * (Translation/, Pivot/, Enum/) are imposed elsewhere and are NOT * affected by this flag — they always nest. */ export declare function nestEditableByGroup(layer: BaseEditableLayer, loc: { path: string; namespace: string; }, group: string | undefined): { path: string; namespace: string; }; /** * Derive full PHP config from optional overrides. * All paths and namespaces fall back to sensible defaults. */ export declare function derivePhpConfig(overrides?: LaravelCodegenOverrides): PhpConfig; /** * Strip PHP comments so a commented-out declaration does not read as a real * one. Without this, the very common * * // protected $fillable = ['name']; // left from a refactor * * would be reported as shadowing the base — and a warning that fires on code * that is not running is worse than no warning, because people learn to ignore * it. Naive on purpose: it can strip a `//` inside a string literal, which only * ever costs us a warning we would otherwise have printed, never a false one. */ export declare function stripPhpComments(source: string): string;