/** * @fileoverview Generic recipe selector union + resolver — the selection * half of the recipe substrate (the registry half lives in `./registry.ts`). * * A recipe selects a named subset of registered units (checks today, * scenarios, rules later). The four common selection shapes — * `explicit` / `all` / `tags` / `pattern` — are structurally identical * across tools; only unit-specific fields (e.g. fitness `config`, sim * `kind`) diverge. This module owns the shared union and a resolver * generic over the kernel `Registerable` surface (`id` / `name` / `tags`). * * **Layer-safe by construction.** Core never names a tool type or pulls a * tool dependency. Everything tool-specific arrives injected: * - `keysOf(item)` — the match keys an item exposes (fitness: namespaced * slug + bare slug + tag/slug targets; sim: `[id, name]`). * - `match(target, pattern)` — the glob matcher for the `pattern` arm * (fitness injects `minimatch`; `minimatch` stays out of core). * - `resolveExplicit(id, items)` — a tool's fallback lookup for an * explicit id that is not itself a primary key (fitness's * bare-slug → namespaced-key resolution). * - `predicates` — a per-arm predicate map for arms core cannot name * (sim's `kind`) **or** arms whose semantics differ from core's * built-in (sim's `tags`, which excludes on id/name rather than tags). * * **Selection only, never execution.** The resolver returns the selected * items; each tool's service consumes them with its own scheduler. This * is the load-bearing seam the symmetric-tool architecture (ADR-0005) * draws — execution stays tool-owned. * * **`tags` semantics.** Core's built-in `tags` arm intersects an item's * tags with `include` and rejects on `exclude` (matching fitness's * `resolveTagsSelector`). A tool whose `tags` arm means something else * (sim excludes on id/name) supplies a `tags` predicate instead, which * overrides the built-in arm — see `predicates` below. */ import type { RecipeUnitConfigMap } from './unit-config.js'; import type { Registerable } from '../lib/registry.js'; /** Select an explicit, ordered list of units by id. */ export interface ExplicitSelector { readonly type: 'explicit'; readonly ids: readonly string[]; readonly config?: RecipeUnitConfigMap; } /** Select every unit, with optional pattern-based exclusions. */ export interface AllSelector { readonly type: 'all'; readonly exclude?: readonly string[]; readonly config?: RecipeUnitConfigMap; } /** Select units whose tags intersect `include` and avoid `exclude`. */ export interface TagsSelector { readonly type: 'tags'; readonly include: readonly string[]; readonly exclude?: readonly string[]; readonly config?: RecipeUnitConfigMap; } /** Select units whose match keys glob-match `include` and avoid `exclude`. */ export interface PatternSelector { readonly type: 'pattern'; readonly include: readonly string[]; readonly exclude?: readonly string[]; readonly config?: RecipeUnitConfigMap; } /** * The generic selector union. Tools alias the `explicit` arm's `ids` to * their historical field name (fitness `checkIds`, sim `scenarioIds`) in * their own `types.ts` so existing recipe literals are unchanged, and may * extend the union with tool-only arms (sim `kind`). */ export type RecipeSelector = ExplicitSelector | AllSelector | TagsSelector | PatternSelector; /** Glob matcher injected by the tool for the `pattern` / `all` arms. */ type Matcher = (target: string, pattern: string) => boolean; /** * Injected, tool-supplied resolution hooks. Keeping these external is what * lets core resolve selectors without naming any tool type. */ export interface ResolveSelectorOptions { /** Match keys for an item — used by the `all` and `pattern` arms. */ readonly keysOf: (item: T) => readonly string[]; /** Tags an item carries — used by the built-in `tags` arm. Defaults to `item.tags ?? []`. */ readonly tagsOf?: (item: T) => readonly string[]; /** Glob matcher for `pattern` / `all` exclude matching. Required only if such an arm reaches the resolver. */ readonly match?: Matcher; /** * Fallback lookup for an `explicit` id that is not itself a primary key * — e.g. fitness's bare-slug → namespaced-key resolution. Returns the * canonical primary key, or `undefined`. When omitted, explicit ids are * matched as literal primary keys only. */ readonly resolveExplicit?: (id: string, items: readonly T[]) => string | undefined; /** * Per-arm predicates for arms core cannot name (sim `kind`) or whose * semantics differ from the built-in arm (sim `tags`). Keyed by arm * `type`; when present for the active arm it overrides the built-in. */ readonly predicates?: Readonly boolean>>; } /** * Resolve a selector against a caller-supplied item list, returning the * selected items in the appropriate order. Pure — no logging, no I/O. * * A tool-supplied predicate for the active arm overrides the built-in * resolution, so a tool whose arm semantics differ (or whose arm core * cannot name) stays byte-exact without core knowing about it. */ export declare function resolveSelector(selector: S, items: readonly T[], opts: ResolveSelectorOptions): readonly T[]; export {}; //# sourceMappingURL=selector.d.ts.map