{
  "version": 3,
  "sources": ["../src/index.ts", "../src/errors.ts", "../src/engine.ts", "../src/rule.ts"],
  "sourcesContent": ["/**\n * A small, zero-dependency, async-native rule-evaluation engine.\n *\n * `Rule<TContext>`/`RulesEngine<TContext>` are generic over the context they\n * read from, with no default type parameter \u2014 dict-context is\n * `FunctionRule<Context>`/`RulesEngine<Context>`.\n *\n * ```ts\n * import { AndRule, FunctionRule, type Context } from \"verdict-rules\";\n *\n * const overEighteen = new FunctionRule<Context>(\"over_18\", async (ctx) => ({\n *   ruleName: \"over_18\",\n *   passed: (ctx.age as number) >= 18,\n * }));\n *\n * const verdict = await new AndRule<Context>(\"eligible\", [overEighteen]).evaluate({ age: 21 });\n * console.log(verdict.passed); // true\n * ```\n */\n\nexport { RulesEngine } from \"./engine.js\";\nexport { UnknownLookupError } from \"./errors.js\";\nexport type { Context, RuleResult, RunResult } from \"./result.js\";\nexport { AndRule, FunctionRule, OrRule } from \"./rule.js\";\nexport type { Rule, RulePredicate } from \"./rule.js\";\n", "/**\n * Thrown when a lookup names a rule or group that does not exist.\n *\n * ```ts\n * try {\n *   await engine.runGroup(\"cor\", ctx);\n * } catch (err) {\n *   if (err instanceof UnknownLookupError && err.kind === \"group\") {\n *     // a typo, not a failed evaluation\n *   }\n * }\n * ```\n */\nexport class UnknownLookupError extends Error {\n  /** Whether the missing thing was a rule name or a group label. */\n  readonly kind: \"rule\" | \"group\";\n\n  /** The name or label that matched nothing. */\n  readonly key: string;\n\n  constructor(kind: \"rule\" | \"group\", key: string) {\n    super(\n      kind === \"rule\"\n        ? `No rule named '${key}' in this engine`\n        : `No rules in group '${key}' in this engine`,\n    );\n    this.name = \"UnknownLookupError\";\n    this.kind = kind;\n    this.key = key;\n  }\n}\n", "import { UnknownLookupError } from \"./errors.js\";\nimport type { RuleResult, RunResult } from \"./result.js\";\nimport type { Rule } from \"./rule.js\";\n\n/**\n * Holds a set of rules and answers questions about them.\n *\n * The engine's run modes never short-circuit; a composite reaches one\n * verdict and stops.\n *\n * Generic over `TContext`, the same way {@link Rule} is, with no default\n * type parameter \u2014 dict-context is `RulesEngine<Context>`.\n */\nexport class RulesEngine<TContext> {\n  readonly #rules: readonly Rule<TContext>[];\n  readonly #byName: Map<string, Rule<TContext>>;\n  readonly #byGroup: Map<string, Rule<TContext>[]>;\n\n  constructor(rules: readonly Rule<TContext>[]) {\n    this.#rules = [...rules];\n    this.#byName = new Map(rules.map((r) => [r.name, r]));\n    this.#byGroup = new Map();\n    for (const rule of rules) {\n      if (rule.group === undefined || rule.group === \"\") continue;\n      const existing = this.#byGroup.get(rule.group);\n      if (existing === undefined) this.#byGroup.set(rule.group, [rule]);\n      else existing.push(rule);\n    }\n  }\n\n  /** Every rule name registered here, in registration order. */\n  get ruleNames(): readonly string[] {\n    return [...this.#byName.keys()];\n  }\n\n  /** Every group label carried by at least one rule, in first-seen order. */\n  get groupNames(): readonly string[] {\n    return [...this.#byGroup.keys()];\n  }\n\n  /** Evaluate every registered rule. Never short-circuits. */\n  async runAll(context: TContext): Promise<RunResult> {\n    const results: RuleResult[] = [];\n    for (const rule of this.#rules) {\n      results.push(await rule.evaluate(context));\n    }\n    return { passed: results.every((r) => r.passed), results };\n  }\n\n  /**\n   * Evaluate one rule by name, or return `undefined` if no such rule exists.\n   *\n   * `undefined` means absent, never failed.\n   */\n  async tryRunNamed(\n    name: string,\n    context: TContext,\n  ): Promise<RuleResult | undefined> {\n    const rule = this.#byName.get(name);\n    if (rule === undefined) return undefined;\n    return rule.evaluate(context);\n  }\n\n  /**\n   * Evaluate exactly one rule, looked up by name.\n   *\n   * @throws {UnknownLookupError} if no rule has this name.\n   */\n  async runNamed(name: string, context: TContext): Promise<RuleResult> {\n    const result = await this.tryRunNamed(name, context);\n    if (result === undefined) {\n      throw new UnknownLookupError(\"rule\", name);\n    }\n    return result;\n  }\n\n  /**\n   * Evaluate a group, or return `undefined` if no such group exists.\n   *\n   * `undefined` means absent, never vacuously passed.\n   */\n  async tryRunGroup(\n    group: string,\n    context: TContext,\n  ): Promise<RunResult | undefined> {\n    const rules = this.#byGroup.get(group);\n    if (rules === undefined || rules.length === 0) return undefined;\n    const results: RuleResult[] = [];\n    for (const rule of rules) {\n      results.push(await rule.evaluate(context));\n    }\n    return { passed: results.every((r) => r.passed), results };\n  }\n\n  /**\n   * Evaluate every rule sharing a group label. Never short-circuits.\n   *\n   * @throws {UnknownLookupError} if no rule carries this label.\n   */\n  async runGroup(group: string, context: TContext): Promise<RunResult> {\n    const result = await this.tryRunGroup(group, context);\n    if (result === undefined) {\n      throw new UnknownLookupError(\"group\", group);\n    }\n    return result;\n  }\n}\n", "import type { RuleResult } from \"./result.js\";\n\n/**\n * The contract every rule satisfies.\n *\n * A plain `interface` \u2014 structural typing: any object of the right shape\n * is a `Rule`. No `implements` clause, no base class, no registration.\n *\n * `Rule<TContext>` is generic over the context it reads from, with no\n * default type parameter. Dict-context is `Rule<Context>`, written out\n * every time.\n *\n * ```ts\n * const overEighteen: Rule<Context> = {\n *   name: \"over_18\",\n *   async evaluate(ctx) {\n *     return { ruleName: \"over_18\", passed: (ctx.age as number) >= 18 };\n *   },\n * };\n * ```\n */\nexport interface Rule<TContext> {\n  /**\n   * Unique identifier for this rule, used for engine lookups and to attribute\n   * a result back to its source.\n   */\n  readonly name: string;\n\n  /** Optional group label. Rules sharing one can be run together. */\n  readonly group?: string | undefined;\n\n  /** Evaluate this rule against `context`. */\n  evaluate(context: TContext): Promise<RuleResult>;\n}\n\n/** Signature of the predicate {@link FunctionRule} wraps. */\nexport type RulePredicate<TContext> = (context: TContext) => Promise<RuleResult>;\n\n/**\n * Wraps a plain async predicate as a {@link Rule}.\n *\n * `TContext` is inferred from the wrapped predicate's own parameter type.\n */\nexport class FunctionRule<TContext> implements Rule<TContext> {\n  readonly name: string;\n  readonly group: string | undefined;\n  readonly #predicate: RulePredicate<TContext>;\n\n  constructor(name: string, predicate: RulePredicate<TContext>, group?: string) {\n    this.name = name;\n    this.group = group;\n    this.#predicate = predicate;\n  }\n\n  /** Runs the wrapped predicate and returns whatever it returns, unchanged. */\n  evaluate(context: TContext): Promise<RuleResult> {\n    return this.#predicate(context);\n  }\n}\n\n/**\n * Composite that passes only if every sub-rule passes.\n *\n * Short-circuits on the first failing sub-rule.\n *\n * An empty list passes vacuously.\n *\n * Every sub-rule must share the exact same `TContext`.\n */\nexport class AndRule<TContext> implements Rule<TContext> {\n  readonly name: string;\n  readonly group: string | undefined;\n  readonly #rules: readonly Rule<TContext>[];\n\n  constructor(name: string, rules: readonly Rule<TContext>[], group?: string) {\n    this.name = name;\n    this.group = group;\n    this.#rules = rules;\n  }\n\n  async evaluate(context: TContext): Promise<RuleResult> {\n    const subResults: RuleResult[] = [];\n    // Sequential, not Promise.all.\n    for (const rule of this.#rules) {\n      const result = await rule.evaluate(context);\n      subResults.push(result);\n      if (!result.passed) {\n        const detail = result.detail\n          ? `'${rule.name}' failed: ${result.detail}`\n          : `'${rule.name}' failed`;\n        return { ruleName: this.name, passed: false, detail, data: subResults };\n      }\n    }\n    return { ruleName: this.name, passed: true, data: subResults };\n  }\n}\n\n/**\n * Composite that passes as soon as any sub-rule passes.\n *\n * Short-circuits on the first passing sub-rule.\n *\n * An empty list fails vacuously. The same same-`TContext` requirement\n * across sub-rules applies here too.\n */\nexport class OrRule<TContext> implements Rule<TContext> {\n  readonly name: string;\n  readonly group: string | undefined;\n  readonly #rules: readonly Rule<TContext>[];\n\n  constructor(name: string, rules: readonly Rule<TContext>[], group?: string) {\n    this.name = name;\n    this.group = group;\n    this.#rules = rules;\n  }\n\n  async evaluate(context: TContext): Promise<RuleResult> {\n    const subResults: RuleResult[] = [];\n    // Sequential, not Promise.all.\n    for (const rule of this.#rules) {\n      const result = await rule.evaluate(context);\n      subResults.push(result);\n      if (result.passed) {\n        return { ruleName: this.name, passed: true, data: subResults };\n      }\n    }\n    return {\n      ruleName: this.name,\n      passed: false,\n      detail: \"no sub-rule passed\",\n      data: subResults,\n    };\n  }\n}\n"],
  "mappings": "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",
  "names": []
}
