/**
 * Normalise rule: `crud-into-curved`.
 *
 * Phase 3 of `2026-05-13-NORMALISE-PHASE.md` §5 Rule 2. Lifts Service
 * operations that match the canonical CRUD shape onto their target
 * entity's Controller `cured:` block. This eliminates redundant service
 * shells for plain CRUD wrappers — SpecVerse generates
 * `cured.retrieve/create/update/delete` from a controller's cured
 * declaration, so an explicit `Service.getClub` that just does
 * "look up by id" is duplicating convention.
 *
 * Schema note: CURVED operations live on Controllers, NOT on Models.
 * If the target entity has no Controller declared, the rule synthesises
 * a `<Entity>Controller` (with `model: <Entity>`) in the same component
 * as the Model. The cured op lands there.
 *
 * Before (post-Rule-1 / cluster-module-functions):
 *
 *   components:
 *     App:
 *       models:
 *         Club:
 *           attributes: [...]
 *       services:
 *         ClubsService:
 *           operations:
 *             getClub:
 *               steps: [Look up Club by id, Return Club or null]
 *               requires: [id is provided]
 *             getClubPositions:                 # NOT lifted (filtered query)
 *               steps: [...]
 *
 * After:
 *
 *   components:
 *     App:
 *       models:
 *         Club:
 *           attributes: [...]
 *       controllers:
 *         ClubController:                       # synthesised by this rule
 *           model: Club
 *           cured:
 *             retrieve:
 *               steps: [Look up Club by id, Return Club or null]
 *               requires: [id is provided]
 *       services:
 *         ClubsService:
 *           operations:
 *             getClubPositions: { ... }        # remains
 *
 * If the service becomes empty after lifting, it is dropped entirely.
 *
 * Conservative matching:
 *   - Op name must be EXACTLY `<verb><EntityName>` — no trailing suffix
 *     (`getRound` ✓, `getRoundsForUser` ✗, `getRoundStatus` ✗)
 *   - Target Entity name must resolve UNIQUELY to ONE model across all
 *     components (the cured block goes to whichever component the model
 *     lives in — cross-component lifts are fine since the cured: lives
 *     ON the model, not at the service site)
 *   - Skip when the entity name is declared in multiple components (rare,
 *     but ambiguous — caller can rename the duplicate)
 *   - Target model's `cured.<curvedOp>` must NOT already be declared
 *     (don't overwrite spec-author intent)
 *   - Verb patterns recognised:
 *       get / retrieve / find → retrieve
 *       create / add          → create   (Phase 3: only `create` to stay safe)
 *       update                → update
 *       delete / remove       → delete   (Phase 3: only `delete` to stay safe)
 *
 * NOT in Phase 3 scope:
 *   - retrieve_many (list / getAll / search — naming varies too widely)
 *   - Evolve transitions (would need lifecycle state-machine signal)
 *   - Cross-component lifts (target model in a different component)
 *   - Multi-arg create variants (createClubWithPositions)
 */
import type { NormaliseRule, NormaliseSpec } from '../normalise-rules.js';
interface CrudMatchData {
    /** Component hosting the source Service. */
    sourceComponentName: string;
    /** Component hosting the target Model (may differ from source). */
    targetComponentName: string;
    modelName: string;
    serviceName: string;
    operationName: string;
    curvedOp: 'retrieve' | 'create' | 'update' | 'delete';
}
/**
 * Parse an operation name into (verb, entityName) if it matches the
 * exact `<verb><PascalCaseEntity>` shape. Returns null otherwise.
 *
 * The entity portion must be PascalCase, contain no further uppercase
 * boundaries after the leading letter (i.e. single PascalCase word),
 * AND must end at the end of the string (no trailing qualifier like
 * `getRoundStatus` or `getClubsForUser`).
 */
declare function parseCrudOpName(opName: string): {
    verb: string;
    curved: CrudMatchData['curvedOp'];
    entity: string;
} | null;
/**
 * Build a model-name → component-name index across all components. Used
 * by the cross-component target lookup. When an entity name is declared
 * in multiple components, it's recorded as ambiguous (the rule then
 * skips matches against that name).
 */
declare function buildModelIndex(spec: NormaliseSpec): Map<string, string | 'ambiguous'>;
declare function detectMatchesInComponent(spec: NormaliseSpec, modelIndex: Map<string, string | 'ambiguous'>, componentName: string, componentBody: Record<string, unknown>): CrudMatchData[];
export declare const CRUD_INTO_CURVED_RULE: NormaliseRule;
export declare const __internal: {
    parseCrudOpName: typeof parseCrudOpName;
    buildModelIndex: typeof buildModelIndex;
    detectMatchesInComponent: typeof detectMatchesInComponent;
};
export {};
//# sourceMappingURL=crud-into-curved.d.ts.map