Escape

quario

The engine. Compiles a definition into a tree of closures and emits an event stream that every render target consumes.

Creating an instance

quario(options?)

signature
function quario(options?: QuarioOptions): Quario

Creates a configured instance. Host-level configuration only.

QuarioOptions

Field Type Meaning
license string License key, verified offline once at creation. The engine marks output when the key ends before this version’s release date.
locale string Locale for format. Defaults to "en-US".
currency string Default ISO 4217 code for format: "currency".
timeZone string Timezone for format: "date". Defaults to "UTC".
query QueryOptions Budgets for the JSONPath selection, checked at construction.
schemes readonly string[] The URL schemes an href may use, lower-case and colon-terminated. Defaults to https: and mailto:. A value the engine can’t read throws a TypeError at construction.

quario() refuses what it doesn’t understand, with a TypeError naming the path, such as options.locale. That covers an unknown key, a value of the wrong type, and an options that isn’t an object. It also covers a locale or timeZone that Intl refuses, and a currency that isn’t three upper-case letters. An options object shared with other code therefore throws, so pass quario() only these six keys.

Quario

signature
interface Quario {
  readonly license: Promise<LicenseInfo>
  report(schema: ReportSchema, functions?: FunctionRegistry): CompiledReport
  plan(schema: unknown, functions?: FunctionRegistry, options?: PlanOptions): Plan
}

interface PlanOptions {
  readonly targets?: readonly TargetCapabilities[]
}

interface TargetCapabilities {
  readonly target: string
  readonly declarations: Readonly<Record<string, string>>
}

license settles with the verification result and never rejects. report throws on the first definition problem. plan returns everything one traversal produced. An entry in functions that isn’t a function is a problem at functions.<name>. plan refuses an unknown key in options the same way, and a targets that isn’t an array throws a TypeError.

options.targets takes the capabilities descriptors the published targets export beside their factories. Every declaration the document marks required that one of them withdraws or leaves unread becomes a problem at required.<name>. An approximated declaration isn’t one, and passing no descriptors checks nothing.

LicenseInfo

signature
interface LicenseInfo {
  licensed: boolean
  licensee?: string
  id?: string
}

Compiling and rendering

CompiledReport

signature
interface CompiledReport {
  stream(
    data?: unknown,
    params?: Readonly<Record<string, JsonValue>>,
  ): Generator<ReportEvent, void, undefined>
  readonly params: readonly DeclaredParam[]
  readonly names: readonly string[]
  readonly readPaths: ReadPaths
  readonly functions: readonly FunctionSignature[]
  readonly paths: readonly QueryPath[]
  render<Out>(
    target: Target<string, Out>,
    data?: unknown,
    params?: Readonly<Record<string, JsonValue>>,
  ): Promise<Awaited<Out>>
}

params supplies values for the parameters the document names. The engine refuses a key the document doesn’t name and a value of the wrong declared type. It refuses a render that leaves out a required parameter before any expression runs.

render resolves the target’s output whether the target renders synchronously or not. A malformed target throws synchronously as a definition error.

render also settles every asynchronous registered function the report calls, before the target reads an event. stream returns events as you pull them, so it can wait for nothing: it reports the call it reached and names the cell. A target therefore never sees a promise, whichever way the host drives the report.

names is the union of free variables excluding engine anchors and group handles. readPaths maps each compiled source’s schema path to the member paths it reads off its anchors. functions lists the registry functions called, in call-first-seen order. paths is the frozen dependency topology of the data query.

signature
type ReadPath = readonly [string, ...(string | null)[]]
type ReadPaths = Readonly<Record<string, readonly ReadPath[]>>

DeclaredParam

signature
interface DeclaredParam {
  readonly name: string
  readonly required: boolean
  readonly type?: ParamType
  readonly default?: JsonValue
  readonly label?: string
}

One entry per declared parameter, in declaration order. type, default and label are present only where the document wrote one, and required is the absence of a default. The list holds a bare value too, as an optional parameter whose default is that value.

Target<Name, Out>

signature
interface Target<Name extends string = string, Out = unknown> {
  name: Name
  compile: (stream: ReportEventStream) => (data?: unknown) => Out
}

The contract every render target honours. The engine calls compile once per render call, so it must be cheap and stateless. Per-render work belongs in the renderer it returns.

FunctionRegistry

signature
type FunctionRegistry = Record<string, (...args: any[]) => any>

Host functions a definition may call. A registered name shadows a built-in of the same name.

Checking a definition

validate(schema, functions?)

signature
function validate(schema: unknown, functions?: FunctionRegistry): string[]

Every problem in the definition, in document order, as located strings. Needs no instance and no data.

Plan

signature
interface Plan {
  readonly report: CompiledReport | null
  readonly params: readonly DeclaredParam[]
  readonly problems: readonly Problem[]
  readonly anchors: Readonly<Record<string, readonly string[]>>
  readonly readPaths: ReadPaths
  readonly uses: Readonly<Partial<Record<RequirableName, readonly string[]>>>
  readonly warnings: readonly Warning[]
}

report is null while the document has problems, and params is there either way, so an editor can draw a parameter form beside the problems. anchors maps a compiled source’s schema path to the anchors and handles it reads. readPaths maps the same paths to the member paths each source reads, ending in null where it reads on through a key the definition doesn’t name. uses maps each name in REQUIRABLE the document writes to the paths that write it. It never adds a problem and needs no option. warnings is advisory, in document order from the same descent.

Problem

signature
interface Problem {
  readonly path: string
  readonly source?: string
  readonly message: string
  readonly diagnostic?: QuarioDiagnostic
}

Frozen. message is exactly the string validate returns for this problem.

Warning

signature
interface Warning {
  readonly path: string
  readonly message: string
}

An advisory says the document declares something nothing will read. Not a Problem at lower severity, and not its shape. It carries no source and no diagnostic, because nothing raised, and the engine decides nothing will read a declaration only after every compile succeeds.

isDiagnostic(e)

signature
function isDiagnostic(e: unknown): e is QuarioDiagnostic

True for a located diagnostic raised by a delegated engine, authenticated by identity. False for quario’s own verdicts and for a registered function’s throw.

QuarioDiagnostic

signature
interface QuarioDiagnostic extends Error {
  readonly code?: QuarioErrorCode
  readonly start?: number
  readonly end?: number
  readonly limit?: number
  readonly actual?: number
  readonly blocks?: readonly SjabloonBlock[]
}

code is absent on option and target-definition faults. limit and actual appear on a budget failure. Diagnostic codes sit outside the frozen authoring surface and may change.

Writing a target

These exports exist for code consuming the event stream. An application rendering through html(), pdf(), xlsx() or csv() needs none of them. Custom targets covers what they’re for and builds a complete target from them.

walk(events, handlers)

signature
function walk(events: Iterable<ReportEvent>, handlers: WalkHandlers): Promise<void>

Dispatches one render’s events to per-event handlers in stream order and once each. It pulls lazily and yields the loop between batches. Handlers are synchronous, and a promise one returns isn’t awaited.

breathe()

signature
function breathe(): Promise<void>

Yields the event loop to the host, and resolves after the host takes its turn.

splits(events)

signature
function splits(events: Iterable<ReportEvent>): Generator<WalkEvent, void>

Folds each split bracket into one split event and passes every other event through unchanged, lazily. Call walk(splits(events), handlers) where you want a split whole rather than a bracket you track yourself.

collapse(events)

signature
function collapse(events: Iterable<WalkEvent>): Generator<WalkEvent, void>

Drops the content of every instance whose group declares collapsed, nested instances whole, and keeps its brackets and its own bands. Call walk(collapse(events), handlers) from a target with nowhere to hide a row a reader could open again. The engine computes aggregates before the stream, so a footer still totals rows nobody sees.

text(tokens) and display(value)

signature
function text(tokens: readonly Token[]): string
function display(value: unknown): string

text joins a token stream to display text. display is the scalar rule it joins with, so a target stringifies exactly as the stream does. A valid Date renders as ISO 8601 UTC, an invalid one stays "Invalid Date", and nullish displays empty.

typed(tokens, decl?)

signature
function typed(
  tokens: readonly Token[],
  decl?: ResolvedFormat | FormatKind,
): number | boolean | Date | undefined

The typed-cell seam. Exactly one value token holding a finite number, a boolean or a valid Date keeps its pre-stringify value. Anything else reports undefined. Pass the resolved format to revive a date string under the date kind. One run of one value token is still a single-token array. The second argument is that token’s run’s resolved format rather than the cell’s.

styledRuns(tokens)

signature
interface TokenRun {
  style: Record<string, unknown> | null
  tokens: Token[]
}
function styledRuns(tokens: Token[]): TokenRun[]

Groups a cell’s tokens into its runs, in order, consecutive tokens with equal styles forming one. style is null where the cell’s own applies. Equality goes one level deep, which is exhaustive rather than approximate, since format is the only object-valued declaration and is itself flat. The built-in targets consume it, so a custom target grouping through it can’t drift from them.

format(value, style?, options?)

signature
function format(
  value: unknown,
  style?: { format?: unknown; currency?: unknown } | null,
  options?: { locale?: string; currency?: string; timeZone?: string } | null,
): string | undefined

Presents a token for a cell’s resolved style. Returns nothing when the kind doesn’t apply, so the caller uses display() instead.

currencyOf(style?, options?)

signature
function currencyOf(
  style?: { currency?: unknown } | null,
  options?: { currency?: string } | null,
): string | null

Which code a cell wears under format: "currency". Branch on this rather than on style.currency ?? instanceCurrency, which gets an unusable code wrong.

fractionDigits(style?, options?)

signature
function fractionDigits(
  style?: { format?: unknown; currency?: unknown } | null,
  options?: { currency?: string } | null,
): number | undefined

How many fraction digits a cell presents. Returns nothing where the cell presents no count.

isReportBand(role)

signature
function isReportBand(role: string | undefined): boolean

Whether a band role names one of the report’s own bands rather than a group instance’s.

hostOptions(value, keys, at) and hostMeta(value, at)

signature
interface QuarioMeta {
  title?: string
  author?: string
  subject?: string
}

function hostOptions<T>(value: T, keys: readonly string[], at: string): T
function hostMeta<T extends QuarioMeta | null | undefined>(value: T, at: string): T

The checks every official target refuses its options with, exported so a target of your own refuses them the same way. hostOptions closes a key set. It throws a TypeError prefixed with at when the value carries a key outside keys. A host’s typo then costs one stack trace rather than an option silently lost. Nullish is absence, and own enumerable keys only, so an inherited property never counts as an option the host wrote.

hostMeta validates the { title, author, subject } contract the document targets share — each a string, and nothing else. It checks the three once here because every document format carries the same three. What each format calls them inside its own package stays that target’s business. Both return the value you give them, so a factory reads its options through them.

imageError(path, said, cause?)

signature
function imageError(path: string | undefined, said: string, cause?: unknown): Error

Mints an image failure named on the item that asked for the bytes. The result takes cause’s class. Not a diagnostic.

pageBox(page?, at?) and paper(page?, opening?, at?)

signature
function pageBox(
  page?: PageOption,
  at?: string,
): { width: number; height: number; margin: number | PageSides }

function paper(
  page?: PageOption,
  opening?: { size?: unknown; margin?: unknown; orientation?: unknown },
  at?: string,
): {
  size: PageOption['size']
  margin: number | PageSides | undefined
  orientation: PageOption['orientation']
  width: number
  height: number
  sides: PageSides
}

interface PageOption {
  size?: 'A4' | 'letter' | [number, number]
  margin?: number | PageSides
  orientation?: 'portrait' | 'landscape'
}

interface PageSides { top: number; right: number; bottom: number; left: number }

The rules every target with a page follows, exported so a target of your own applies them the same way. pageBox validates a host’s page option and returns its box. paper resolves the option against the document’s opening event. The document’s key wins, the host’s fills a gap, and both declaring one key throws. It returns the declared keys, undefined where nobody declared one, plus width, height and sides in points, turned by the orientation. Both throw a TypeError prefixed with at, "options.page" by default.

The vocabularies as data

signature
const STYLE_NAMES: readonly string[]
const RUN_STYLE_NAMES: readonly string[]
const REPORT_STYLE_NAMES: readonly string[]
const REQUIRABLE: readonly RequirableName[]
const ZERO_DASH: string
function isBoxName(name: string): boolean

const FORMAT_VOCABULARY: {
  readonly kinds: readonly FormatKind[]
  readonly forms: readonly DateForm[]
  readonly negatives: readonly NegativeReading[]
  readonly zeroes: readonly ZeroReading[]
  readonly modifiers: Readonly<Record<FormatKind, readonly string[]>>
  readonly defaultForm: DateForm
  readonly defaultNegative: NegativeReading
  readonly defaultZero: ZeroReading
  readonly maxDigits: number
}

The closed vocabularies as data, for a tool that offers them rather than checks them. RUN_STYLE_NAMES is the inline half a styled run may wear, in the vocabulary’s own order, and REPORT_STYLE_NAMES the report default’s two names. REQUIRABLE is the set a document may mark required, and the exported RequirableName is that set as a type. A marking no target could answer for is therefore an error where an author writes it, rather than a plan problem afterwards. A name outside the set is a definition error either way. isBoxName answers whether a style name is a padding or a border side, the partition that defines a table row’s box. ZERO_DASH is what a cell reads as under zero: "dash", stated once because the XLSX target writes the same character into its sectioned number format.

FORMAT_VOCABULARY.modifiers names every modifier a kind takes rather than one of them. A surface offering them then needs no knowledge that the three number kinds share theirs and date doesn’t.