import { Unique } from "./constraints"; import { Field } from "./field"; import { Index } from "./indices"; import { Association } from "./relationship"; /** * Operations that can have scope enforcement applied */ export type ScopeOperation = "find" | "get" | "create" | "update" | "delete"; /** * Configuration options for scope enforcement behavior */ export interface ScopeOptions { /** * If true (default), scope fields are automatically injected from * the request context on create when not provided. */ injectOnCreate?: boolean; /** * Which operations should enforce scope. Defaults to all operations. */ enforceOn?: ScopeOperation[]; /** * Roles allowed to bypass scope enforcement (e.g., 'system_admin'). */ bypassRoles?: string[]; } export interface Entity { name: string; /** * Explicit database table name. When set, it overrides the default table * name derived from `name` (snake_case). Use it to avoid SQL reserved words * (e.g. an entity `Order` deriving to the reserved word `order`). */ table?: string; created_at?: boolean; updated_at?: boolean; primaryKeyType?: "serial" | "uuid"; fields?: Field[]; indexes?: Index[]; uniques?: Unique[]; /** * Fields or paths that define the authorization scope for this entity. * Can be a single field name (e.g., 'workspaceId') or an array of fields/paths * (e.g., ['workspaceId', 'serviceId'] or ['task.workspaceId']). * * - Direct fields: 'workspaceId' - enforces WHERE workspaceId = ctx.workspaceId * - Nested paths: 'task.workspaceId' - joins to related entity and enforces scope */ scopeBy?: string | string[]; /** * Optional configuration for how scope enforcement should behave. */ scopeOptions?: ScopeOptions; /** * When true, state changes to this entity (insert/update/remove) emit a * durable DomainEvent row written in the SAME database transaction as the * change (transactional-outbox pattern, surfaced as generic "domain events"). * * Resolution: the effective value is `entity.emitEvents ?? ?? false`. * This means a top-level `emitEvents: true` enables it for every entity, while * an individual entity can opt out with `emitEvents: false`. */ emitEvents?: boolean; /** * Whether to generate the HTTP controller for this entity. Defaults to true. * When false, the generator still emits the entity, service, DTOs, and a module * (with `TypeOrmModule.forFeature` + the service), but omits the controller — * leaving the HTTP surface to a hand-written controller without a route * collision with the generated CRUD. * * Per-framework behavior (the flag is generic, but only matters where a * generated route can't be cleanly overridden): * - **NestJS** and **Gin (Go)**: actually suppresses the controller/route — * these frameworks can't override a generated route (Nest controllers aren't * DI-swappable; Gin panics on duplicate paths). Implemented for NestJS; * Go is tracked separately. * - **FastAPI**: effectively a **no-op** — FastAPI matches routes in * registration order, so an app overrides by registering its router ahead of * the generated `autogen_router` (no suppression needed). * * Resolution: the effective value is `entity.http ?? ?? true`, so * controllers are on by default and opted out per entity (or globally). */ http?: boolean; associations?: Association[]; }