> ## Documentation Index
> Fetch the complete documentation index at: https://niceeval.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Report API

> Public boundaries for ReportScope, FactRequirement, ReportPlan, ReportInput, single execution, and static export.

## Fixed call chain

```ts theme={null}
let plan: ReportPlan;
let input: ReportInput;
{
  await using record = await openRecordReader({ root });
  const sample = await projectExplicitRuns(record, { runIds });
  const scope = createReportScope(sample);
  plan = definition.plan(scope);
  input = await buildReportInput({ record, sample, plan });
}

const execution = executeReport({ definition, plan, input });
```

Only `buildReportInput()` receives a `RecordReader`. A Report definition, Calculation, Page, Download, local view, and static runtime receive neither a reader nor a path.

## ReportScope

```ts theme={null}
interface ReportScope {
  readonly provenance: AnalysisProjectionProvenance;
  readonly runs: readonly AnalysisRun[];
  readonly slots: readonly AnalysisSlot[];
}
```

`runs` exhausts every selected Run. `slots` is the complete core-only denominator and preserves `included`, `not-recorded`, `invalid`, and `excluded`. An included slot has its Attempt ID, origin, locator, and Member kind, but no business facts.

## FactRequirement

```ts theme={null}
type FactOwner = "run" | "attempt";

interface FactRequirement<Value> {
  readonly id: string;
  readonly owner: FactOwner;
  readonly name: string;
  readonly source: BuiltInFactSource | CustomJsonFactSource<Value>;
}
```

NiceEval exports built-in sources. Define a custom fact with:

```ts theme={null}
defineJsonFact({
  id,
  owner,
  name,
  parse(document) {
    return value;
  },
});
```

A custom document is exactly `{ observedAt, value }` with media type `application/json`. One owner/name has one value. Its parser never receives bytes, paths, or blobs. The build phase validates transport only; execution calls a synchronous parser once for each owner and requirement combination.

## ReportPlan

```ts theme={null}
interface ReportPlan {
  readonly calculations: readonly Calculation<unknown>[];
  readonly pages: readonly ReportPage[];
  readonly downloads: readonly ReportDownload[];
}
```

A plan has no top-level facts or resources. Each consumer declares its own inputs. A parameterized page must list every concrete route in the plan.

## ReportInput

```ts theme={null}
interface ReportInput {
  readonly scope: ReportScope;
  readonly sample: AnalysisSample;
  // The internal fact matrix uses an unexported brand and cannot be indexed by user code.
}

interface ReportContext {
  readonly scope: ReportScope;
  readonly sample: ReportSample; // Explicitly excludes recordRoot
}
```

Each internal read retains its `ChannelRead` state—`read | unavailable | unsupported | invalid`—and completeness. A consumer receives fact-free context plus `readRun(runId, fact)` and `readAttempt(includedSlot, fact)`, which validate its declared `inputs`. Separate requirement objects cannot reuse an ID.

The context's `sample` is a `ReportSample` projection without `recordRoot`. A Calculation, Page, or Download cannot obtain a Record path from public fields.

## Calculation

```ts theme={null}
interface Calculation<Value> {
  readonly id: string;
  readonly inputs: readonly FactRequirement<ReportJsonValue>[];
  readonly completeness: "allowPartial" | "requireComplete";
  evaluate(input: CalculationInput): CalculationValue<Value>;
}
```

A Calculation can read only its inputs. `allowPartial` preserves the complete denominator and actual observed count. `requireComplete` requires both durable collection and decoding to be complete.

## Page

```ts theme={null}
interface ReportPage {
  readonly id: string;
  readonly route: ReportRoute;
  readonly title: string;
  readonly inputs: readonly FactRequirement<ReportJsonValue>[];
  readonly calculations: readonly Calculation<unknown>[];
  render(input: ReportPageInput): ReportPageModel;
}

interface ReportPageModel {
  readonly title: string;
  readonly body: ReportJsonValue;
  readonly textAlternative: string;
}
```

A PageModel is ordinary serializable data. Color, graphics, or interaction cannot replace text, tables, and state descriptions.

## Download

```ts theme={null}
interface ReportDownload {
  readonly id: string;
  readonly path: ReportDownloadPath;
  readonly mediaType: string;
  readonly inputs: readonly FactRequirement<ReportJsonValue>[];
  readonly calculations: readonly Calculation<unknown>[];
  build(input: ReportDownloadInput): Uint8Array;
}
```

Create a Download path with `defineReportDownloadPath("downloads/...")`. Its bytes are produced completely in the single execution phase and never recomputed during export.

## ReportExecution

```ts theme={null}
interface ReportExecution {
  readonly plan: ReportPlan;
  readonly input: ReportInput;
  readonly calculations: readonly CalculationResult<unknown>[];
  readonly pages: readonly PageResult[];
  readonly downloads: readonly DownloadResult[];
}
```

Every result distinguishes success, `input-invalid`, and `execution-failed`. `executeReport()` runs every consumer at most once. Local view displays failure locally, while export performs a whole-execution preflight.

Calculation results retain their originating Calculation and correspond one-to-one with plan order.

## StaticAssetManifest

```ts theme={null}
interface StaticAssetManifest {
  readonly routes: readonly {
    readonly route: ReportRoute;
    readonly pagePath: StaticOutputPath;
    readonly hostDataPath: StaticOutputPath;
  }[];
  readonly entries: readonly StaticAsset[];
}
```

The manifest is always `manifest.json` and does not list itself in `entries`. Every other file has exactly one entry. A static directory contains only already-generated pages, host data, downloads, and the exporter's built-in runtime, base styles, and fonts.

Page and host-data filenames follow plan order. Built-in resources live only under `runtime/`; user downloads live only under `downloads/`. Routes and output paths use canonical ASCII segments of at most 240 bytes. Their comparison key is bytewise ASCII lowercase. NiceEval checks exact/key duplicates and directory-prefix conflicts before creating the temporary directory.

`exportStaticReport({ execution, out })` requires an absent `out`. It never replaces a directory, executes Report code, reads a Record, accesses the network, or looks up an arbitrary user path.
