import type { CollectionSchemaBase, SchemaBase } from './dsl.js'; import type { DtoMessage } from './dto.js'; import type { EntitySchema } from './entity.js'; import type { FrontAppSchema, ProjectApiSchema } from './project.js'; // Schema-collection integration: multiple source collections combine into // one target collection (e.g. two entities into one dto, or entity columns // plus a dto into one third-party wire message). // // A convert file binds to ONE source identity — an entity (internal mapping, // {Table}Convert.ts) or a third-party service (anti-corruption translation, // {third-service}.convert.ts) — and holds N methods keyed by name (same // shape as defineService / defineDao). Table schemas are not a convert // source: tables must be projected into an entity (row contract) first. /** A source/target collection of a convert — dto or entity. Entity sources * may carry aggregate fields (aggField), e.g. an aggregate result entity * projected into a wire message. */ export type ConvertSourceSchema = DtoMessage | EntitySchema; /** Method input for defineConvert: type/schema/name are set by the builder. */ export type ConvertMethodDef = Omit; /** One conversion: multiple source collections → single target collection. */ export interface ConvertMethodSchema extends SchemaBase { type: 'convertMethod'; /** The convert file this method belongs to. */ schema: ConvertSchema; /** Source schemas — one or more, mixed dimensions. */ sources: ConvertSourceSchema[]; /** Target schema — the single integrated collection. */ target: ConvertSourceSchema; } /** Declares multi-source → single-target schema integrations grouped by source identity. */ export interface ConvertSchema extends CollectionSchemaBase { type: 'convert'; /** The backend api module this convert belongs to (shared instance from * project.config.ts apis). Storage is convert_schema/{api.name}/{app.name}/ * — same layout as service_schema. */ api: ProjectApiSchema; /** The app (module) this convert belongs to — its artifact lands in modules/{app}/convert/. */ app: FrontAppSchema; /** Methods keyed by name — the map key is written back as the method name. */ methods: Record; } export function defineConvert(options: { name: string; api: ProjectApiSchema; app: FrontAppSchema; methods: Record; description?: string; }): ConvertSchema { if (Object.keys(options.methods).length === 0) { throw new Error(`convert '${options.name}': methods must not be empty`); } if (!options.api.apps.includes(options.app)) { throw new Error(`convert '${options.name}': api '${options.api.name}' does not serve app '${options.app.name}'`); } const schema: ConvertSchema = { type: 'convert', name: options.name, description: options.description, api: options.api, app: options.app, methods: {}, }; for (const key of Object.keys(options.methods)) { const method = options.methods[key] as ConvertMethodDef; if (method.sources.length === 0) { throw new Error(`convert '${options.name}': method '${key}' sources must not be empty`); } schema.methods[key] = { type: 'convertMethod', schema, ...method, name: key }; } return schema; }