/** * @fileoverview Generic recipe registry — shared plumbing for fitness * and simulation recipe registries. * * Both fitness and simulation expose a "recipe" concept (a named bundle * of selectors + execution options). The registry data structure they * need is identical: a Map by id, a Map by name, register/lookup * helpers, and a guarded duplicate-id policy. Only the recipe service — * how a recipe's selector is resolved against the corresponding tool's * registry of checks/scenarios — legitimately differs between the two * tools, so the service stays per-package. * * Implemented on top of the kernel's unified `Registry` base. * `RecipeRegistry` exposes the historical `allowOverwrite + * throwOnDuplicate` flag-pair surface and routes per-call to the * appropriate `inner.register(...)` invocation. * * The temp `protected byId`/`byName` shim that Phase 2 introduced is * gone — both subclasses (`FitnessRecipeRegistry`, * `SimulationRecipeRegistry`) now seed built-ins via * `registerAll(builtIns, { internal: true })` (LSP-clean). */ import { Registry, type Registerable } from '../lib/registry.js'; import type { Logger } from '../lib/logger.js'; /** Minimum shape any recipe must satisfy to live in a `RecipeRegistry`. */ export interface RecipeBase extends Registerable { readonly id: string; readonly name: string; readonly displayName: string; readonly description: string; readonly tags?: readonly string[]; } /** Options for a single `register` call. */ export interface RecipeRegisterOptions { /** Allow replacing an existing entry with the same id or name. */ readonly allowOverwrite?: boolean; /** * Throw a `ValidationError` on duplicate id/name instead of the * default "warn-and-skip" behaviour. Use this for registries where * historical callers relied on a thrown error (e.g. user recipe * loaders that signal config errors via exceptions). */ readonly throwOnDuplicate?: boolean; /** * Validation-error code surfaced when `throwOnDuplicate` fires. * Falls back to the registry's `validationCode` constructor option. */ readonly validationCode?: string; /** * Bypass the duplicate guard for this call. Used by built-in * seeding paths in `FitnessRecipeRegistry` / `SimulationRecipeRegistry`; * not part of the public surface for user code. */ readonly internal?: boolean; } /** Constructor options for a `RecipeRegistry`. */ export interface RecipeRegistryOptions { /** Human label used in log/throw messages — e.g. `'fitness'`, `'simulation'`. */ readonly module?: string; /** Default validation error code on duplicate when `throwOnDuplicate` is set. */ readonly validationCode?: string; readonly logger?: Logger; } /** * Process-wide policy: duplicate id/name with `allowOverwrite: false` * keeps the first entry and emits a warning. Use `register(.., { * throwOnDuplicate: true })` to opt into the historical fitness/sim * "throw on duplicate" contract. */ export declare class RecipeRegistry { protected readonly inner: Registry; private readonly module; private readonly validationCode; constructor(options?: RecipeRegistryOptions); /** * Register a recipe. * * - Default: refuses on duplicate id/name; logs a `recipe.registry.duplicate` warning. * - `{ allowOverwrite: true }`: replaces the existing entry. * - `{ throwOnDuplicate: true }`: throws a `ValidationError` instead of warning. * Mutually exclusive with `allowOverwrite`. * - `{ internal: true }`: bypasses the duplicate guard. Used for * built-in seeding in subclasses. */ register(recipe: T, options?: RecipeRegisterOptions): void; /** Register many recipes with shared options. */ registerAll(recipes: readonly T[], options?: RecipeRegisterOptions): void; /** Look up a recipe by name first, falling back to id. */ loadRecipe(nameOrId: string): T | undefined; getByName(name: string): T | undefined; getById(id: string): T | undefined; has(nameOrId: string): boolean; /** All registered recipes, in registration order. */ getAllRecipes(): readonly T[]; /** All registered recipe names, in registration order. */ getNames(): readonly string[]; /** Recipes with a given tag. */ getByTag(tag: string): readonly T[]; get size(): number; /** Remove a recipe by id. Returns true if it existed. */ remove(id: string): boolean; /** Drop every entry. */ clear(): void; } //# sourceMappingURL=registry.d.ts.map