---
url: https://peter-gy.github.io/refkit/index.md
description: >-
Parse, render, inspect, format, and edit bibliography data from Python,
JavaScript, and Polars.
---
---
---
url: https://peter-gy.github.io/refkit/get-started.md
description: >-
Install RefKit in Python or TypeScript and render the same citation and
bibliography.
---
# Get Started
RefKit parses a bibliography and applies a [Citation Style Language](https://citationstyles.org/) (CSL) style to produce citations and a bibliography. Choose a language tab to follow the same workflow in Python or [TypeScript](https://www.typescriptlang.org/), JavaScript with checked types.
## Install
Use Python 3.10 through 3.14 or [Node.js](https://nodejs.org/) 22.19 or newer. The package managers [pip](https://pip.pypa.io/) and [npm](https://docs.npmjs.com/) install the corresponding binding:
::: code-group
```bash [Python]
python -m pip install refkit
```
```bash [TypeScript]
npm install refkit-js
```
:::
Node.js initializes RefKit during import. Browser applications [initialize the WebAssembly module](/guides/browser#initialize-the-module) before using the same objects.
::: details Building Python from source
Pip selects a compatible wheel when available. A source build requires Rust and Git and retrieves pinned revisions of the [Hayagriva bibliography engine](https://github.com/typst/hayagriva) and [Citationberg CSL model](https://github.com/typst/citationberg). The first build needs network access unless those revisions are cached.
:::
## Render a citation
Save the example as `first_citation.py` or `first_citation.ts`:
::: code-group
```python [Python]
import refkit as rk
source = """
@article{doe2024,
author = {Doe, Jane},
title = {Fast Citations},
journal = {Journal of Citation Tests},
year = {2024}
}
"""
library = rk.Library.parse_bibtex(source)
document = rk.Document(library, rk.Style.load("apa"), locale="en-US")
rendered = document.render([rk.Citation("intro", "doe2024")])
print(rendered["intro"].text)
print(rendered.bibliography.text)
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = `
@article{doe2024,
author = {Doe, Jane},
title = {Fast Citations},
journal = {Journal of Citation Tests},
year = {2024}
}
`;
const library = rk.Library.parseBibtex(source);
const document = new rk.Document(library, rk.Style.load("apa"), { locale: "en-US" });
const rendered = document.render([new rk.Citation("intro", "doe2024")]);
console.log(rendered.get("intro").text);
console.log(rendered.bibliography.text);
```
:::
Run the file:
::: code-group
```bash [Python]
python first_citation.py
```
```bash [TypeScript]
node first_citation.ts
```
:::
Both produce:
```text
(Doe, 2024)
Doe, J. (2024). Fast Citations. Journal of Citation Tests.
```
`Library` holds normalized references. `Style` selects the citation rules. `Document` combines them with a locale, and each `Citation` names a result. `RenderedDocument` gives you the named citations and the bibliography of entries they cite.
## Choose the next task
| Task | Continue with |
| --- | --- |
| Understand normalized records and editable source | [Choose a bibliography model](/concepts/bibliography-models) |
| Inspect entries, fields, and diagnostics | [Parse bibliographies](/guides/parse-bibliographies) |
| Render groups, locators, and ordered citations | [Render citations](/guides/render-citations) |
| Preserve comments and duplicate occurrences during edits | [Edit raw BibTeX](/guides/edit-bibtex) |
| Canonically format a bibliography | [Format BibTeX](/guides/format-bibtex) |
| Process Python dataframe columns | [Use Polars expressions](/guides/polars) |
[How RefKit works](/concepts/how-refkit-works) connects these tasks through the shared object model.
---
---
url: https://peter-gy.github.io/refkit/why-refkit.md
description: >-
Understand why RefKit separates normalized bibliography data, raw BibTeX, and
styled output.
---
# Why RefKit
Bibliography work often crosses three different representations: source text that people edit, normalized records that programs query, and styled output that documents display. RefKit gives each representation one owner and keeps their transitions in a portable Rust core.
## Keep source and meaning separate
A normalized entry is convenient for selection and rendering. It cannot preserve every comment, preamble, string definition, malformed block, delimiter, or duplicate occurrence from a `.bib` file.
RefKit exposes both contracts:
* `Library` owns normalized entries and parser diagnostics.
* `BibDocument` owns source-order raw BibTeX and edit-preserving writeback.
The split makes the tradeoff explicit at construction time. A render workflow starts with `Library`. A source repair starts with `BibDocument`.
## Render a complete citation operation
Citation styles can sort items, disambiguate names and years, and change a bibliography based on the citations that appear in a document. `Document.render` accepts the complete ordered citation list for one operation and returns named citations with their cited bibliography.
Separate `Document.render` calls create separate render state. This keeps one result reproducible from its library, style, locale, and ordered citations.
## Inspect rendered structure
Every `Rendered` value exposes plain text, HTML, and a structured tree. The tree keeps links, formatting, display metadata, and bibliography entry identity available to another renderer without parsing HTML.
## Process columns without Python object loops
`polars-refkit` registers Rust-backed expressions on `pl.Expr.refkit`. Each bibliography source row becomes an independent parse and render boundary inside an eager query or lazy plan.
The Polars interface returns native scalar, list, and struct columns. Report expressions keep parser or formatter details next to the row that produced them.
## Run the same core in several hosts
The Rust core accepts in-memory values and returns RefKit-owned records. Python owns paths, objects, and exceptions. TypeScript exposes the same objects through WebAssembly in Node.js and browsers. Polars owns expression registration, broadcasting, dtypes, and row failures. PyEmscripten wheels carry the Python interfaces into Pyodide.
This ownership model keeps bibliography behavior in one implementation while each host exposes native inputs and outputs.
Read [How RefKit Works](/concepts/how-refkit-works) for the object and state model.
---
---
url: https://peter-gy.github.io/refkit/performance.md
description: >-
Measure RefKit capabilities with fixed inputs, correctness checks, isolated
workers, and reproducible baseline comparisons.
---
# Performance
RefKit's benchmark measures public bibliography operations with fixed inputs and checked outputs. Choose the capability that controls your workload: parsing, entry lookup, BibTeX edits, citation rendering, formatting, or Polars batch execution.
## Measure a capability
The runner lives in `packages/refkit-bench` in the [source repository](https://github.com/peter-gy/refkit). Follow the [benchmark setup and methodology](https://github.com/peter-gy/refkit/blob/main/development_docs/benchmarks.md) to install the participants and build release-mode adapters.
From that checkout:
```bash
uv run --no-sync refkit-bench check --lane parse.bibtex --dataset real
uv run --no-sync refkit-bench run --lane parse.bibtex --dataset real \
--output packages/refkit-bench/results/baseline
uv run --no-sync refkit-bench report packages/refkit-bench/results/baseline
```
This case parses the same 12-record bibliography through RefKit, [bibtexparser](https://github.com/sciunto-org/python-bibtexparser), and [Pybtex](https://pybtex.org/). Validation checks complete metadata. Timings cover in-memory model creation, with validation outside the timer.
## Read the evidence
[pyperf](https://pyperf.readthedocs.io/), a Python benchmarking tool, calibrates batches and collects repeated values in separate workers. A result directory contains:
* `manifest.json`: input and artifact hashes, source provenance, setup boundaries, validation outcomes, runtime settings, and host identity.
* `timings.json`: measured values, calibration, warmups, and worker metadata in pyperf's format.
The summary reports the median of worker means for each case. Compare matched runs with:
```bash
uv run --no-sync refkit-bench compare \
packages/refkit-bench/results/baseline \
packages/refkit-bench/results/candidate
```
The ratio is candidate time divided by baseline time. Values below 1 mean lower elapsed time. With enough independent workers, the report includes exploratory per-case bootstrap intervals. Repeat small effects in alternating baseline/candidate sessions on the same idle machine before attributing a gain to an implementation change.
## Match the workflow
Parsing, rendering, formatting, and dataframe execution have different units and setup costs. Compare within a lane and dataset. The shared rendering fixture checks exact basic author-date output. Polars batch citation cases use the plugin's bundled APA style. The formatter corpus checks exact expectations from a pinned [bibtex-tidy](https://github.com/FlamingTempura/bibtex-tidy) revision and records differences from the published package.
The methodology guide lists every lane, its timed boundary, corpus coverage, known conformance differences, and commands for scaling experiments. A failed correctness check prevents that selected run from producing a timing comparison.
---
---
url: https://peter-gy.github.io/refkit/concepts/how-refkit-works.md
description: >-
Follow RefKit's normalize, render, and raw-edit flows through their state
owners.
---
# How RefKit Works
RefKit moves bibliography data through three flows. Python and JavaScript expose the same state owners and results.
## Normalize bibliography input
`Library` is the normalized citation database. It accepts [BibTeX](https://www.bibtex.org/) or [BibLaTeX](https://ctan.org/pkg/biblatex), text formats for bibliography entries, and [Hayagriva YAML](https://github.com/typst/hayagriva/blob/main/docs/file-format.md), a structured bibliography format. Each entry exposes a citation key, entry type, common fields, and normalized parent records.
Normalization resolves bibliography syntax into records suited to selection, projection, and rendering. `Library.diagnostics` records messages from recoverable BibTeX parsing. Python and Node.js also provide file-reading APIs. Browsers supply source text to the same in-memory parsing operations.
## Preserve raw BibTeX
`BibDocument` scans the original source into ordered blocks. An occurrence is one entry or field at one source position. Occurrence identity keeps duplicate entry keys and duplicate field names individually addressable.
Assigning `BibField.value` changes the document state shared by its entry and field handles. Serializing the document produces updated BibTeX text with unrelated raw blocks retained. Write that text through the host's filesystem or browser download API when the workflow needs a file.
## Render an ordered citation document
`Style` prepares a [Citation Style Language (CSL)](https://citationstyles.org/) style, a format that describes citation and bibliography rules. `Locale` validates and stores a bundled locale code. The renderer loads that locale's terms when a `Document` operation starts.
`Document.render` accepts ordered, named `Citation` objects. A `CitationGroup` holds one or more `Cite` items. The order can affect disambiguation and the cited bibliography.
```text
Library + Style + Locale + ordered Citation values
↓
Document
↓
named citations + cited bibliography
```
Each output is `Rendered`. It exposes the same result as plain text, escaped HTML, and a structured render tree. Each render operation owns fresh citation state. Python and JavaScript reclaim object resources automatically when the objects become unreachable.
## Adapt the core to a host
The `refkit-core` [Rust](https://www.rust-lang.org/) library owns parsing, recovery, raw syntax, formatting, styles, and rendering. Adapters translate those capabilities into host-native values:
| Host | Adapter owns |
| --- | --- |
| Python | Paths, Python objects, exceptions, native module registration, and one-call helpers. |
| JavaScript | Typed objects, [WebAssembly](https://webassembly.org/) initialization for the compiled core, and Node.js file helpers. |
| [Polars](https://docs.pola.rs/), a DataFrame query engine | Expressions, broadcasting, column data types, plugin loading, and row failure mapping. |
| [Pyodide](https://pyodide.org/), Python running in a browser | The Python interfaces packaged as wheels for its WebAssembly runtime. |
Continue with [Choose a Bibliography Model](/concepts/bibliography-models) to choose between normalized data and raw source.
---
---
url: https://peter-gy.github.io/refkit/concepts/bibliography-models.md
description: >-
Choose Library for normalized behavior or BibDocument for source-preserving
BibTeX edits.
---
# Choose a Bibliography Model
`Library` stores normalized citation data. `BibDocument` preserves [BibTeX](https://www.bibtex.org/) source, a text format for bibliography entries. Choose the model from the information the workflow must retain.
## Use `Library` for normalized data
::: code-group
```python [Python]
import refkit as rk
library = rk.Library.parse_bibtex("@article{doe2024, title={Fast Citations}, year={2024}}")
entry = library["doe2024"]
print(entry.key, entry.entry_type, entry.title)
```
```ts [TypeScript]
import * as rk from "refkit-js";
const library = rk.Library.parseBibtex("@article{doe2024, title={Fast Citations}, year={2024}}");
const entry = library.get("doe2024")!;
console.log(entry.key, entry.entryType, entry.title);
```
:::
Both print `doe2024 Article Fast Citations`. TypeScript examples run in Node.js. For browsers, complete [browser initialization](/guides/browser#initialize-the-module) before calling the same APIs.
`Library` owns normalized entries, parent relationships, key lookup, selection, projection, and parser diagnostics. Pass a library to the renderer when a workflow needs citations or a bibliography. Normalization discards source layout such as whitespace and field delimiters.
## Use `BibDocument` for source-preserving edits
Using the `rk` import, parse and edit a title:
::: code-group
```python [Python]
source = """% reviewed by Jane
@article{doe2024,
title = {Old title},
year = {2024}
}
"""
document = rk.BibDocument.parse(source)
document.entries["doe2024"].fields["title"].value = "Corrected title"
print(document.to_bibtex())
```
```ts [TypeScript]
const source = `% reviewed by Jane
@article{doe2024,
title = {Old title},
year = {2024}
}
`;
const document = rk.BibDocument.parse(source);
document.entries.getUnique("doe2024")!.fields.getUnique("title")!.value = "Corrected title";
console.log(document.toBibtex());
```
:::
The output contains `title = {Corrected title}` and retains the comment, spacing, and year field. `BibDocument` keeps source-order blocks, entry and field occurrences, and byte spans. Field assignments validate the replacement against the original delimiter mode before changing the document.
## Address duplicates by occurrence
An occurrence is one entry or field at one source position. A unique lookup raises `RefkitError` when the name has multiple occurrences. Retrieve the occurrences to choose which one to edit:
::: code-group
```python [Python]
duplicates = rk.BibDocument.parse("""
@article{same, title={First}}
@article{same, title={Second}}
""")
second = duplicates.entries.get_all("same")[1]
second.fields["title"].value = "Updated second title"
print(duplicates.to_bibtex())
```
```ts [TypeScript]
const duplicates = rk.BibDocument.parse(`
@article{same, title={First}}
@article{same, title={Second}}
`);
const second = duplicates.entries.getAll("same")[1]!;
second.fields.getUnique("title")!.value = "Updated second title";
console.log(duplicates.toBibtex());
```
:::
The first title stays `First`. The second becomes `Updated second title`. Entry and field maps expose both unique names and source-order occurrences. See the [Python](/reference/python#raw-bibtex) and [TypeScript](/reference/javascript#raw-bibtex) references for lookup and missing-name behavior.
## Move between the models deliberately
The models own separate state. Parse the edited document's writeback into a new library when rendering must reflect an edit:
::: code-group
```python [Python]
updated = rk.Library.parse_bibtex(document.to_bibtex())
print(updated["doe2024"].title)
```
```ts [TypeScript]
const updated = rk.Library.parseBibtex(document.toBibtex());
console.log(updated.get("doe2024")!.title);
```
:::
Both print `Corrected title`.
Continue with [Parsing and Recovery](/concepts/parsing-and-recovery) or [Edit Raw BibTeX](/guides/edit-bibtex).
---
---
url: https://peter-gy.github.io/refkit/concepts/citation-rendering.md
description: >-
Build an ordered citation document and inspect its citations, bibliography,
HTML, text, or render tree.
---
# Citation Rendering
Citation rendering transforms normalized entries through a [Citation Style Language (CSL)](https://citationstyles.org/) style, a format that describes citation and bibliography rules. One render operation owns the full ordered citation list and the bibliography derived from it.
## Build a citation document
::: code-group
```python [Python]
import refkit as rk
library = rk.Library.parse_bibtex("""
@article{doe2024, author={Doe, Jane}, title={Fast Citations}, year={2024}}
@book{roe2022, author={Roe, Richard}, title={Batch References}, year={2022}}
""")
document = rk.Document(library, rk.Style.load("apa"), locale="en-US")
rendered = document.render(
[
rk.Citation("intro", "doe2024"),
rk.Citation(
"detail",
rk.CitationGroup(
[
rk.Cite("doe2024", locator="12", label="page"),
"roe2022",
]
),
),
]
)
print(rendered["intro"].text)
print(rendered["detail"].text)
```
```ts [TypeScript]
import * as rk from "refkit-js";
const library = rk.Library.parseBibtex(`
@article{doe2024, author={Doe, Jane}, title={Fast Citations}, year={2024}}
@book{roe2022, author={Roe, Richard}, title={Batch References}, year={2022}}
`);
const document = new rk.Document(library, rk.Style.load("apa"), { locale: "en-US" });
const rendered = document.render([
new rk.Citation("intro", "doe2024"),
new rk.Citation("detail", new rk.CitationGroup([
new rk.Cite("doe2024", { locator: "12", label: "page" }),
"roe2022",
])),
]);
console.log(rendered.get("intro").text);
console.log(rendered.get("detail").text);
```
:::
Both produce `(Doe, 2024)` for `intro` and `(Doe, 2024, p. 12; Roe, 2022)` for `detail`. TypeScript examples run in Node.js. For browsers, complete [browser initialization](/guides/browser#initialize-the-module) first.
The objects describe the document at distinct levels:
| Object | Owns |
| --- | --- |
| `Cite` | One citation key and optional locator such as a page number. |
| `CitationGroup` | One or more items rendered as one citation cluster. |
| `Citation` | A group, a unique result ID, and an optional document note number. |
| `Document` | A library, style, and locale prepared for rendering. |
| `RenderedDocument` | Named citation outputs and the cited bibliography. |
## Keep the full order together
Each render call creates fresh citation state. Pass the complete ordered citation list when citations depend on earlier disambiguation or numbering. The result preserves the input IDs in citation order and supports lookup by ID, as `intro` and `detail` demonstrate.
## Choose a bibliography boundary
The `rendered` result includes the entries cited by that call. The document can also produce a bibliography of every entry in the library:
::: code-group
```python [Python]
print(rendered.bibliography.text)
print(document.full_bibliography().text)
```
```ts [TypeScript]
console.log(rendered.bibliography.text);
console.log(document.fullBibliography().text);
```
:::
Both bibliographies contain Doe and Roe because this citation list cites both entries. A document's cited-bibliography method accepts a citation list and returns its bibliography directly. See the [Python](/reference/python#rendering) and [TypeScript](/reference/javascript#rendering) references for the exact methods.
## Choose an output representation
Every `Rendered` value exposes `text` for plain text, `html` for escaped HTML, and `tree` for structured nodes and bibliography records. The tree retains formatting, links, and source identity for a host renderer. Bibliography outputs also provide `layout`, including hanging indent, label alignment, and line and entry spacing. Read [Data Shapes](/reference/data-shapes) for the node contract.
## Load styles and locales
`Style.load(name)` resolves a bundled style such as `apa`, `ieee`, or `chicago-author-date`. To prepare a custom style, parse CSL XML text or read a local style file through the host's file API. The [Python](/reference/python#styles-and-citations) and [TypeScript](/reference/javascript#styles-and-citation-inputs) references describe both paths.
`Locale.load(code)` validates a bundled locale, which supplies translated terms and date conventions. A document accepts a locale code directly, as `en-US` demonstrates. Pass a `Locale` object when validation should happen before document construction.
Continue with [Render Citations](/guides/render-citations) for complete rendering workflows.
---
---
url: https://peter-gy.github.io/refkit/concepts/parsing-and-recovery.md
description: >-
Choose exact normalized parsing, report recovery, raw failed blocks, or Polars
row diagnostics.
---
# Parsing and Recovery
Parsing can require an exact normalized result or retain recoverable entries with diagnostics. Raw parsing uses a separate block model so malformed source remains inspectable. [BibTeX](https://www.bibtex.org/) stores bibliography entries as text and supports named abbreviations for field values.
## Require an exact normalized result
Normalized BibTeX parsing defaults to the `error` recovery policy. A parser diagnostic raises `ParseError`. The abbreviation `unknown_title` has no definition in this source:
::: code-group
```python [Python]
import refkit as rk
source = "@book{good,title={Known title}}\n@book{bad,title=unknown_title}"
try:
rk.Library.parse_bibtex(source)
except rk.ParseError as error:
print(error.diagnostics[0]["code"])
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = "@book{good,title={Known title}}\n@book{bad,title=unknown_title}";
try {
rk.Library.parseBibtex(source);
} catch (error) {
if (!(error instanceof rk.ParseError)) throw error;
console.log(error.diagnostics[0]!.code);
}
```
:::
Both print `unknown_abbreviation`. TypeScript examples run in Node.js. For browsers, complete [browser initialization](/guides/browser#initialize-the-module) first.
## Retain recoverable entries
The `report` policy retains recovered entries and records diagnostics. Using the same `source`, the parser treats `unknown_title` as literal text:
::: code-group
```python [Python]
library = rk.Library.parse_bibtex(source, recovery="report")
print(library["bad"].title)
for diagnostic in library.diagnostics:
print(diagnostic["code"], diagnostic["action"])
```
```ts [TypeScript]
const library = rk.Library.parseBibtex(source, { recovery: "report" });
console.log(library.get("bad")!.title);
for (const diagnostic of library.diagnostics) {
console.log(diagnostic.code, diagnostic.action);
}
```
:::
Both print `unknown_title`, followed by `unknown_abbreviation literalized`. Each diagnostic also carries a message and an optional byte span into the UTF-8 source. Byte offsets can differ from character indices for non-ASCII text.
A non-empty source that yields no entries and has recovery diagnostics raises `ParseError`. Empty or comment-only input can produce an empty library.
## Input limits
Bibliography source is limited to 16 MiB of UTF-8 text. BibTeX parsing also bounds recursive work:
| Work | Limit |
| --- | --- |
| Value nesting | 64 levels |
| Macro or bibliography-reference depth | 64 levels |
| Expanded bibliography data | 16 MiB |
| Dependency traversal | 100,000 steps |
| Report recovery | 128 changes |
A limit failure produces a `resource_limit` diagnostic and raises `ParseError` under both recovery policies. Split large independent bibliographies or simplify deeply nested values before retrying. Tidy applies the same source-size and value-nesting guards before recursive formatting.
## Inspect malformed raw blocks
`BibDocument.parse` scans the source into blocks even when individual blocks are malformed. Using the `rk` import, inspect an entry with an unclosed value:
::: code-group
```python [Python]
raw = rk.BibDocument.parse("@book{broken,title={Unclosed")
for block in raw.failed_blocks:
print(block["span"], block["raw"])
```
```ts [TypeScript]
const raw = rk.BibDocument.parse("@book{broken,title={Unclosed");
for (const block of raw.failedBlocks) {
console.log(block.span, block.raw);
}
```
:::
The failed block retains `@book{broken,title={Unclosed` and its byte span `[0, 28]`. Each failed block also carries the parser error. The document's block list keeps failed blocks in source order.
## Distinguish diagnostics, warnings, and errors
| Term | Produced by | Meaning |
| --- | --- | --- |
| Diagnostic | Normalized BibTeX parsing | A record identifying the affected source, action, and parser message. |
| Warning | BibTeX formatting | A structured, non-fatal missing-key or duplicate-entry result. |
| Error | Parsing, formatting, rendering, or I/O | The requested operation could not produce its contract. |
[Polars](https://docs.pola.rs/), a DataFrame query engine, applies the same recovery policies per bibliography source row. Read [Process Polars Columns](/guides/polars) for row results and query-level failures.
---
---
url: https://peter-gy.github.io/refkit/guides/parse-bibliographies.md
description: >-
Parse bibliography text, inspect normalized entries, project records, and
select entries by structure in Python or TypeScript.
---
# Parse Bibliographies
`Library` parses [BibTeX](https://ctan.org/pkg/bibtex) and [BibLaTeX](https://ctan.org/pkg/biblatex) bibliography text into normalized `Entry` objects.
::: code-group
```python [Python]
import refkit as rk
source = (
"@article{doe2024, title={Fast Citations}, journal={Citation Tests}, volume={12}, year={2024}}"
)
library = rk.Library.parse_bibtex(source)
print(library.get("doe2024").title)
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = "@article{doe2024, title={Fast Citations}, journal={Citation Tests}, volume={12}, year={2024}}";
const library = rk.Library.parseBibtex(source);
console.log(library.get("doe2024")!.title);
```
:::
```text
Fast Citations
```
TypeScript examples run in Node.js after [installation](/get-started). In a browser, [initialize RefKit](/guides/browser#initialize-the-module) before parsing. The remaining examples continue with `library`.
For [Hayagriva YAML](https://github.com/typst/hayagriva#file-format), a bibliography format with nested entry relationships, use `Library.parse_yaml(source)` in Python or `Library.parseYaml(source)` in TypeScript.
## Inspect entries
::: code-group
```python [Python]
print(len(library))
print("doe2024" in library)
entry = library["doe2024"]
print(entry.key, entry.entry_type, entry.title)
```
```ts [TypeScript]
console.log(library.size);
console.log(library.has("doe2024"));
const entry = library.get("doe2024")!;
console.log(entry.key, entry.entryType, entry.title);
```
:::
`get(key)` returns `None` in Python or `null` in TypeScript for a missing key. Python indexing raises `KeyError` when the key is absent. `get_many(keys)` / `getMany(keys)` preserves the requested order and raises when a requested key is absent.
Use `values()` for normalized entries in library order. `is_empty()` / `isEmpty()` checks whether the library contains any entries.
## Project records
`Library.project` returns a list of Python dictionaries or an array of TypeScript objects. Select fields and optionally limit and order the entries:
::: code-group
```python [Python]
rows = library.project(["key", "entry_type", "title", "date", "doi", "volume"])
selected_rows = library.project(["key", "title"], keys=["doe2024"])
print(selected_rows[0]["title"])
```
```ts [TypeScript]
const rows = library.project(["key", "entryType", "title", "date", "doi", "volume"]);
const selectedRows = library.project(["key", "title"], { keys: ["doe2024"] });
console.log(selectedRows[0]!.title);
```
:::
`type` is an alias for the entry type under that output key. Title, date, DOI, and volume values can be `None` / `null`. [Data Shapes](/reference/data-shapes) defines the normalized fields.
## Select by bibliography structure
`Library.select` uses Hayagriva selectors to match normalized entries and their parent relationships:
::: code-group
```python [Python]
periodical_articles = library.select("article > periodical[volume]")
print([entry.key for entry in periodical_articles])
```
```ts [TypeScript]
const periodicalArticles = library.select("article > periodical[volume]");
console.log(periodicalArticles.map(entry => entry.key));
```
:::
This selector returns `doe2024`, whose periodical parent has a volume. Read [Selectors](/reference/selectors) for the grammar and result behavior.
## Read a file
Python's path methods and the [Node.js](https://nodejs.org/) filesystem helpers select the parser from the extension. These examples read an existing `references.bib` file:
::: code-group
```python [Python]
file_library = rk.Library.read("references.bib")
print(file_library.keys())
```
```ts [TypeScript]
import { readLibrary } from "refkit-js/node";
const fileLibrary = await readLibrary("references.bib");
console.log(fileLibrary.keys());
```
:::
| Extension | Input |
| --- | --- |
| `.bib` | BibTeX or BibLaTeX source. |
| `.yaml`, `.yml` | Hayagriva bibliography YAML. |
RefKit decodes UTF-8 first. A file that requires the Windows-1252-compatible fallback receives a parser diagnostic so the encoding decision stays visible.
## Keep recoverable entries
Report recovery keeps entries that can be parsed and records diagnostics beside them:
::: code-group
```python [Python]
recovered = rk.Library.parse_bibtex(source + "\n@book{broken", recovery="report")
for diagnostic in recovered.diagnostics:
print(diagnostic["code"], diagnostic["message"])
```
```ts [TypeScript]
const recovered = rk.Library.parseBibtex(source + "\n@book{broken", { recovery: "report" });
for (const diagnostic of recovered.diagnostics) {
console.log(diagnostic.code, diagnostic.message);
}
```
:::
The recovered library retains `doe2024` and reports the malformed trailing block. The file readers accept the same recovery option. Keep the default `"error"` policy when subsequent work requires an exact parse. [Recovery](/concepts/parsing-and-recovery) explains the diagnostic fields and recovery boundaries.
---
---
url: https://peter-gy.github.io/refkit/guides/render-citations.md
description: >-
Render named citations and cited or full bibliographies with bundled or
explicit CSL styles in Python or TypeScript.
---
# Render Citations
`Document` renders an ordered sequence of citations against a `Library`, a [Citation Style Language](https://citationstyles.org/) (CSL) style, and a locale. CSL defines citation and bibliography formatting, while the locale supplies language-specific terms.
## Render named citations
::: code-group
```python [Python]
import refkit as rk
source = """
@article{doe2024, author={Doe, Jane}, title={Fast Citations}, year={2024}}
@book{roe2022, author={Roe, Richard}, title={Citation Practice}, year={2022}}
"""
library = rk.Library.parse_bibtex(source)
document = rk.Document(library, rk.Style.load("apa"), locale="en-US")
result = document.render(
[
rk.Citation("opening", "doe2024"),
rk.Citation(
"details",
rk.CitationGroup(
[
rk.Cite("doe2024", locator="12", label="page"),
"roe2022",
]
),
),
]
)
print(result["opening"].text)
print(result["details"].text)
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = `
@article{doe2024, author={Doe, Jane}, title={Fast Citations}, year={2024}}
@book{roe2022, author={Roe, Richard}, title={Citation Practice}, year={2022}}
`;
const library = rk.Library.parseBibtex(source);
const document = new rk.Document(library, rk.Style.load("apa"), { locale: "en-US" });
const result = document.render([
new rk.Citation("opening", "doe2024"),
new rk.Citation("details", new rk.CitationGroup([
new rk.Cite("doe2024", { locator: "12", label: "page" }),
"roe2022",
])),
]);
console.log(result.get("opening").text);
console.log(result.get("details").text);
```
:::
```text
(Doe, 2024)
(Doe, 2024, p. 12; Roe, 2022)
```
TypeScript examples run in Node.js. In a browser, [initialize RefKit](/guides/browser#initialize-the-module) before creating the library. The remaining examples continue with `library`, `document`, and `result`.
Citation IDs identify rendered results and must be unique inside one call. Pass the complete ordered sequence to preserve numbering, subsequent-citation rules, disambiguation, and bibliography contents. Each render call starts fresh citation state.
## Add a locator
`Cite` accepts a citation key plus a `locator` and `label`:
::: code-group
```python [Python]
page = rk.Cite("doe2024", locator="12", label="page")
```
```ts [TypeScript]
const page = new rk.Cite("doe2024", { locator: "12", label: "page" });
```
:::
The style controls the visible form. An unknown locator label raises `ValueError` in Python or `RangeError` in TypeScript during rendering.
## Cite a numbered note
Give a citation its document note number when the style uses note distance or first-reference note numbers:
::: code-group
```python [Python]
note = rk.Citation("detail-note", page, note_number=8)
```
```ts [TypeScript]
const note = new rk.Citation("detail-note", page, { noteNumber: 8 });
```
:::
The note number belongs to the citation occurrence. Pass the complete ordered sequence to `Document.render` so repeated citations can use earlier notes.
## Render a bibliography
The render result includes the cited bibliography. To request a bibliography directly, choose cited entries or the complete library:
::: code-group
```python [Python]
print(result.bibliography.text)
cited = document.cited_bibliography([rk.Citation("opening", "doe2024")])
complete = document.full_bibliography()
```
```ts [TypeScript]
console.log(result.bibliography.text);
const cited = document.citedBibliography([new rk.Citation("opening", "doe2024")]);
const complete = document.fullBibliography();
```
:::
`cited` contains entries referenced by the supplied citations. `complete` contains every entry in the library. Both results expose `.text`, `.html`, `.tree`, and `.layout`.
## Render one citation
The convenience helper renders one citation with a named or prepared style. Python's helper reads a bibliography path. TypeScript's helper accepts a prepared `Library`:
::: code-group
```python [Python]
citation = rk.cite("references.bib", "doe2024", style="ieee")
print(citation.text)
```
```ts [TypeScript]
const citation = rk.cite(library, "doe2024", { style: "ieee" });
console.log(citation.text);
```
:::
Pass a `Cite` or `CitationGroup` for richer input. Use `Document` when several citations share document order.
## Load an explicit style
`Style` accepts an independent CSL style as XML text:
::: code-group
```python [Python]
csl_xml = """"""
custom_style = rk.Style.from_xml(csl_xml)
custom_document = rk.Document(library, custom_style, locale="en-US")
print(custom_document.render([rk.Citation("title", "doe2024")])["title"].text)
```
```ts [TypeScript]
const cslXml = ``;
const customStyle = rk.Style.fromXml(cslXml);
const customDocument = new rk.Document(library, customStyle, { locale: "en-US" });
console.log(customDocument.render([new rk.Citation("title", "doe2024")]).get("title").text);
```
:::
This style renders `Fast Citations`. For a UTF-8 `.csl` file, use `Style.from_path(path)` in Python or `await readStyle(path)` from `refkit-js/node` in Node.js.
Custom XML is limited to 2 MiB, 100,000 XML nodes, 64 nested elements, and 256 attributes per element, including namespace declarations. Expanded rendering is limited to 100,000 elements and 64 levels of combined element nesting and macro calls. Missing, duplicate, or cyclic macros and exceeded limits raise `ValueError` / `RangeError`. A dependent style that requires parent resolution also raises an error. Use `Style.load(name)` for a style in the [bundled archive](/concepts/citation-rendering).
## Render safely for the web
`Rendered.html` emits CSL markup and escapes bibliography data. Link nodes allow safe URL schemes. A URL with an unsafe scheme remains visible as text.
Use `Rendered.tree` when an application needs to control element creation and styling. [Render Structured Output](/guides/render-output) shows a complete custom text consumer and explains source identity, formatting, and bibliography layout.
---
---
url: https://peter-gy.github.io/refkit/guides/render-output.md
description: >-
Read rendered nodes in a custom consumer while preserving bibliography labels,
layout, and source identity.
---
# Render Structured Output
`Rendered.tree` lets an application consume citation output directly. Each node identifies text, formatting, links, nesting, or bibliography entries. This text consumer handles every node kind:
::: code-group
```python [Python]
import refkit as rk
from refkit.types import BibliographyEntry, RenderedNode
def visible_text(node: RenderedNode | BibliographyEntry) -> str:
match node["kind"]:
case "Text" | "Link":
return node["text"]
case "Markup":
return node["value"]
case "Element":
return "".join(visible_text(child) for child in node["children"])
case "bibliography-entry":
label = visible_text(node["label"]) if node["label"] else ""
content = "".join(visible_text(child) for child in node["content"])
return f"{label} {content}".strip()
case "Transparent":
return ""
raise ValueError(f"Unknown node kind: {node['kind']}")
library = rk.Library.parse_bibtex(
"@article{doe2024, author={Doe, Jane}, title={Fast Citations}, year={2024}}"
)
document = rk.Document(library, rk.Style.load("apa"), locale="en-US")
result = document.render([rk.Citation("intro", "doe2024")])
text = "".join(visible_text(node) for node in result["intro"].tree)
print(text)
assert text == "(Doe, 2024)"
assert result.bibliography.layout is not None
```
```ts [TypeScript]
import * as rk from "refkit-js";
import type { BibliographyEntry, RenderedNode } from "refkit-js";
function visibleText(node: RenderedNode | BibliographyEntry): string {
switch (node.kind) {
case "Text":
case "Link":
return node.text;
case "Markup":
return node.value;
case "Element":
return node.children.map(visibleText).join("");
case "bibliography-entry": {
const label = node.label ? visibleText(node.label) : "";
const content = node.content.map(visibleText).join("");
return `${label} ${content}`.trim();
}
case "Transparent":
return "";
}
}
const library = rk.Library.parseBibtex(
"@article{doe2024, author={Doe, Jane}, title={Fast Citations}, year={2024}}",
);
const document = new rk.Document(library, rk.Style.load("apa"), { locale: "en-US" });
const result = document.render([new rk.Citation("intro", "doe2024")]);
const text = result.get("intro").tree.map(visibleText).join("");
console.log(text);
```
:::
```text
(Doe, 2024)
```
TypeScript examples run in Node.js. In a browser, [initialize RefKit](/guides/browser#initialize-the-module) before creating the library.
Use `.text` when the application needs the prepared plain-text result. A tree consumer can retain or transform specific nodes, create its own elements, or connect the output to a source inspector.
## Apply formatting and layout
Text and link nodes carry `formatting`. Map its finite values to the host's formatting system, such as `Italic` to an italic text run and `SmallCaps` to a small-caps style. An element's `display` describes a layout role such as `LeftMargin` or `RightInline`.
A bibliography node separates `label` from `content`. Render the label once, then the content. Read `result.bibliography.layout` for hanging indent, second-field alignment, line spacing, and entry spacing. Apply these settings to the bibliography container or its paragraph styles.
[Data Shapes](/reference/data-shapes) lists every field and accepted formatting value.
## Retain source identity
An element's `meta` can identify its bibliography entry, cite-item index, name role, or name index. Read the metadata `kind` before accessing fields specific to that shape. The bibliography entry's `key` connects rendered output to `library.get(key)`.
`Transparent` nodes carry a citation index and formatting metadata. They contribute no visible text.
## Create HTML safely
`Rendered.html` supplies escaped HTML with allowed links. A custom HTML renderer owns its escaping and URL policy:
* Escape text, markup values, and attribute values.
* Create links for `http`, `https`, and `mailto` URLs when matching RefKit's HTML policy.
* Render other URL schemes as visible text.
A tree `Markup` node is data, not permission to insert trusted HTML. Keep node handling explicit when mapping it into a browser framework or document format.
---
---
url: https://peter-gy.github.io/refkit/guides/edit-bibtex.md
description: >-
Inspect raw BibTeX blocks and duplicate occurrences, then edit field values
while preserving source layout in Python or TypeScript.
---
# Edit Raw BibTeX
`BibDocument` preserves source-order [BibTeX](https://ctan.org/pkg/bibtex) blocks while field values change. Use it when comments, preambles, strings, malformed blocks, duplicate occurrences, delimiters, or surrounding layout must remain available.
## Update a field value
::: code-group
```python [Python]
import refkit as rk
source = "@article{doe2024, title={Fast Citations}, year={2024}}"
document = rk.BibDocument.parse(source)
document.entries["doe2024"].fields["title"].value = "Corrected title"
print(document.to_bibtex())
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = "@article{doe2024, title={Fast Citations}, year={2024}}";
const document = rk.BibDocument.parse(source);
document.entries.getUnique("doe2024")!.fields.getUnique("title")!.value = "Corrected title";
console.log(document.toBibtex());
```
:::
```bibtex
@article{doe2024, title={Corrected title}, year={2024}}
```
The field handle updates the shared `BibDocument`. Serializing the document preserves the surrounding source layout. TypeScript examples run in Node.js. In a browser, [initialize RefKit](/guides/browser#initialize-the-module) before parsing. The remaining examples continue with `document`.
Field assignment validates the replacement against the original delimiter mode. An unsafe replacement raises `ValueError` in Python or `RangeError` in TypeScript before the document changes.
## Resolve fields for inspection
`resolve()` returns detached entry records with string macros and `#`
concatenations expanded in every field. Custom field names are included.
Literal TeX grouping and escapes remain available for downstream processing.
::: code-group
```python [Python]
resolved_document = rk.BibDocument.parse(r"""
@string{topic = {Visual {Data}}}
@article{guide, title = {A } # topic # { Guide \& Examples}, year = 2024}
""")
print(resolved_document.resolve()[0]["fields"]["title"])
```
```ts [TypeScript]
const resolvedDocument = rk.BibDocument.parse(String.raw`
@string{topic = {Visual {Data}}}
@article{guide, title = {A } # topic # { Guide \& Examples}, year = 2024}
`);
console.log(resolvedDocument.resolve()[0]!.fields.title);
```
:::
```text
A Visual {Data} Guide \& Examples
```
Each call uses the current field edits and leaves the document unchanged.
Entry keys retain their case. Entry types and field names are lowercase.
Macro names are case-insensitive. Definitions can appear after their uses,
and the last definition wins. Built-in month names such as `jan` expand to
`January` unless the document defines that macro.
`crossref` and `xdata` values are resolved as field text. Use `Library` when
you need inherited citation metadata and normalized names or dates.
Undefined or cyclic macros reached from entry fields, duplicate entry keys or
fields, malformed entry or string-definition syntax, and expansion-limit
violations raise `ParseError` with diagnostics. Resolution
diagnostic spans refer to the current serialized source. See
[resolved entry records](/reference/data-shapes#resolved-entry-records) for the
result shape and its Polars representation.
## Inspect source-order blocks
::: code-group
```python [Python]
for block in document.blocks:
print(block["kind"], block["span"])
```
```ts [TypeScript]
for (const block of document.blocks) {
console.log(block.kind, block.span);
}
```
:::
Block kinds are `whitespace`, `comment`, `preamble`, `string`, `entry`, `failed`, and `other`. Every span is a half-open pair of UTF-8 byte offsets into the decoded source text. Spans keep their original positions after edits.
Use `comments`, `preamble`, `strings`, and `failed_blocks` / `failedBlocks` for focused views. `blocks` remains the complete source-order view.
## Address duplicate entries
A key can identify several source occurrences. Parse a document with duplicate keys to inspect and update each occurrence separately:
::: code-group
```python [Python]
duplicates = rk.BibDocument.parse("""
@article{doe2024, title={First title}}
@article{doe2024, title={Second title}}
""")
matches = duplicates.entries.get_all("doe2024")
second = matches[1]
second.fields["title"].value = "Revised second title"
print(matches[0].fields["title"].value)
```
```ts [TypeScript]
const duplicates = rk.BibDocument.parse(`
@article{doe2024, title={First title}}
@article{doe2024, title={Second title}}
`);
const matches = duplicates.entries.getAll("doe2024");
const second = matches[1]!;
second.fields.getUnique("title")!.value = "Revised second title";
console.log(matches[0]!.fields.getUnique("title")!.value);
```
:::
The first occurrence still contains `First title`. The entry map exposes these lookups:
| Python | TypeScript | Result |
| --- | --- | --- |
| `unique_keys()` | `uniqueKeys()` | Each distinct key once. |
| `occurrence_keys()` | `occurrenceKeys()` | One key per source occurrence. |
| `occurrences()` | `occurrences()` | Source-order entry handles. |
| `get_all(key)` | `getAll(key)` | Every exact entry-key match. |
| `get_unique(key)` | `getUnique(key)` | One match, or `None` / `null` when missing. Duplicate matches raise `RefkitError`. |
`BibFieldMap` provides the same operations for fields. Entry keys are case-sensitive. Field-name matching is case-insensitive, and unique field keys are normalized to lowercase.
## Inspect entry and field identity
::: code-group
```python [Python]
entry = document.entries["doe2024"]
print(entry.key, entry.kind, entry.span)
field = entry.fields["title"]
print(field.name, field.value, field.span)
```
```ts [TypeScript]
const entry = document.entries.getUnique("doe2024")!;
console.log(entry.key, entry.kind, entry.span);
const field = entry.fields.getUnique("title")!;
console.log(field.name, field.value, field.span);
```
:::
`BibEntry.kind` retains the raw entry type spelling. A normalized `Library` exposes `Entry.entry_type` / `Entry.entryType`. [Bibliography Models](/concepts/bibliography-models) explains when to choose each object.
## Handle malformed blocks
::: code-group
```python [Python]
damaged = rk.BibDocument.parse(source + "\n@book{broken")
for block in damaged.failed_blocks:
print(block["error"])
print(block["raw"])
```
```ts [TypeScript]
const damaged = rk.BibDocument.parse(source + "\n@book{broken");
for (const block of damaged.failedBlocks) {
console.log(block.error);
console.log(block.raw);
}
```
:::
Malformed blocks stay in the document and its serialized output.
::: warning Percent-comment boundary
The raw parser recognizes a complete `@...` block that begins later on a percent-comment line as a live block. Inspect `BibDocument.blocks` before tidying source that embeds complete BibTeX entries inside `%` comments. Normalized report recovery suppresses those embedded blocks, while the formatter can render them as entries.
:::
## Read and write a file
Read an existing `references.bib`, update a field, and save a separate file:
::: code-group
```python [Python]
file_document = rk.BibDocument.read("references.bib")
file_document.entries["doe2024"].fields["title"].value = "Corrected title"
file_document.write("references.edited.bib")
```
```ts [TypeScript]
import { readBibDocument } from "refkit-js/node";
import { writeFile } from "node:fs/promises";
const fileDocument = await readBibDocument("references.bib");
fileDocument.entries.getUnique("doe2024")!.fields.getUnique("title")!.value = "Corrected title";
await writeFile("references.edited.bib", fileDocument.toBibtex(), "utf8");
```
:::
File readers try UTF-8, then a Windows-1252-compatible fallback and record that choice in `diagnostics`. Original file bytes and decoded-text offsets can differ. Both examples write UTF-8.
Use [Format BibTeX](/guides/format-bibtex) for whole-document normalization according to formatting options.
---
---
url: https://peter-gy.github.io/refkit/guides/format-bibtex.md
description: >-
Format BibTeX strings, raw documents, or files and inspect warnings and key
renames in Python or TypeScript.
---
# Format BibTeX
The formatter normalizes [BibTeX](https://ctan.org/pkg/bibtex) source and returns the formatted text, entry count, structured warnings, and key renames.
## Format a string
::: code-group
```python [Python]
import refkit as rk
source = "@ARTICLE {doe2024, pages={6-13}, year={2024},}\n"
result = rk.tidy_bibtex(source)
print(result.bibtex)
assert result.count == 1
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = "@ARTICLE {doe2024, pages={6-13}, year={2024},}\n";
const result = rk.tidyBibtex(source);
console.log(result.bibtex);
```
:::
```bibtex
@article{doe2024,
pages = {6--13},
year = {2024}
}
```
The default formatter lowercases entry and field names, aligns values at column 14, uses two spaces for indentation, normalizes page ranges, escapes supported Unicode and LaTeX-sensitive text, tidies comments, and keeps the first occurrence of each field name.
TypeScript examples run in Node.js. In a browser, [initialize RefKit](/guides/browser#initialize-the-module) before formatting. The remaining examples continue with `source`.
## Choose formatting options
::: code-group
```python [Python]
options = rk.TidyOptions(sort_fields=True, wrap=88, trailing_commas=True)
formatted = rk.tidy_bibtex(source, options=options)
```
```ts [TypeScript]
const options: rk.TidyOptions = {
sortFields: true,
wrap: 88,
trailingCommas: true,
};
const formatted = rk.tidyBibtex(source, { options });
```
:::
Several options accept a boolean shorthand or an explicit value. `wrap=True` / `wrap: true` uses 80 columns, while `88` selects 88. Enabling field sorting uses the canonical order. A list of field names supplies a custom order.
Read [Tidy Options](/reference/tidy-options) for every argument, default, and accepted form.
## Format a raw document
::: code-group
```python [Python]
document = rk.BibDocument.parse(source)
formatted_document = document.tidy(options=options)
```
```ts [TypeScript]
const document = rk.BibDocument.parse(source);
const formattedDocument = document.tidy({ options });
```
:::
`BibDocument.tidy` formats the current in-memory document state. It returns `TidyResult` and leaves file output to the caller.
## Read and optionally write a file
Format an existing `references.bib` and write its result to a separate path:
::: code-group
```python [Python]
formatted_file = rk.tidy_file(
"references.bib",
output="references.formatted.bib",
options=options,
)
```
```ts [TypeScript]
import { tidyFile } from "refkit-js/node";
const formattedFile = await tidyFile("references.bib", {
output: "references.formatted.bib",
options,
});
```
:::
Omit `output` to return the result and leave the filesystem unchanged.
## Inspect warnings
::: code-group
```python [Python]
warnings_result = rk.tidy_bibtex(
"@book{first,doi={10.1/same}}\n@book{second,doi={10.1/same}}",
options=rk.TidyOptions(duplicates=["doi"]),
)
for warning in warnings_result.warnings:
print(warning.code, warning.rule)
```
```ts [TypeScript]
const warningsResult = rk.tidyBibtex(
"@book{first,doi={10.1/same}}\n@book{second,doi={10.1/same}}",
{ options: { duplicates: ["doi"] } },
);
for (const warning of warningsResult.warnings) {
console.log(warning.code, warning.rule);
}
```
:::
Both examples print `duplicate_entry doi`. Each warning also includes a `message` explaining the affected entries. `missing_key` warns about an entry lacking a citation key. `duplicate_entry` includes the matching duplicate rule.
Formatting a malformed block raises `TidySyntaxError`. Its `line`, `column`, `byte`, `character`, and `message` properties locate the parser failure.
## Generate keys and update references
::: code-group
```python [Python]
renamed = rk.tidy_bibtex(
"@book{draft, author={Doe, Jane}, title={Fast Citations}, year={2024}}",
options=rk.TidyOptions(generate_keys=True, sort=True),
)
for rename in renamed.renames:
print(rename["entry_id"], rename["old_key"], rename["new_key"])
```
```ts [TypeScript]
const renamed = rk.tidyBibtex(
"@book{draft, author={Doe, Jane}, title={Fast Citations}, year={2024}}",
{ options: { generateKeys: true, sort: true } },
);
for (const rename of renamed.renames) {
console.log(rename.entryId, rename.oldKey, rename.newKey);
}
```
:::
The rename record maps `draft` to `doe2024fast`. Key generation and merging share one plan. Final keys are unique, bibliography `crossref` and `xdata` fields point to those keys, and key sorting uses the final names. The rename report lets an application update citations in other files. Inspect the report before writing a bibliography used by an existing document.
---
---
url: https://peter-gy.github.io/refkit/guides/polars.md
description: >-
Parse, inspect, render, format, broadcast, and diagnose bibliography source
inside Polars queries.
---
# Process Polars Columns
`polars-refkit` applies bibliography capabilities inside Polars queries. Each bibliography source row forms an independent normalized library and render boundary.
## Install and register the namespace
```bash
python -m pip install polars-refkit
```
Importing `polars_refkit` registers `pl.Expr.refkit`. The same operations are also exported as top-level functions from `polars_refkit`.
## Parse and inspect rows
```python
import polars as pl
import polars_refkit
frame = pl.DataFrame(
{
"bibtex": ["@article{doe2024, title={Fast Citations}, year={2024}}"],
"key": ["doe2024"],
}
)
result = frame.select(
count=pl.col("bibtex").refkit.entry_count(),
keys=pl.col("bibtex").refkit.keys(),
entries=pl.col("bibtex").refkit.entries(fields=["key", "entry_type", "title"]),
)
assert result["count"].to_list() == [1]
assert result["keys"].to_list() == [["doe2024"]]
```
A plain string argument names a column. Use `pl.lit(...)` for literal bibliography source or citation keys.
## Render citations
```python
result = frame.select(
citation=pl.col("bibtex").refkit.cite("key"),
citation_html=pl.col("bibtex").refkit.cite("key", output="html"),
rendered=pl.col("bibtex").refkit.cite("key", output="rendered"),
)
```
Choose the citation shape from the key input:
| Operation | Key value | Result per row |
| --- | --- | --- |
| `cite` | `String` | One citation. |
| `cite_each` | `List[String]` | One separate citation per key, in order. |
| `cite_group` | `List[String]` | One grouped citation containing the ordered keys. |
Choose `output="text"`, `"html"`, or `"rendered"` for strings or `{text, html}` structs. `full_bibliography` renders every normalized entry in the row.
## Broadcast a Singleton Input
Citation operations accept equal-length inputs or a length-one input on either side. A singleton valid bibliography source is parsed once within that expression and reused for every key row.
```python
source = "@article{doe2024, author={Doe, Jane}, year={2024}}"
result = pl.DataFrame({"key": ["doe2024", "doe2024"]}).select(pl.lit(source).refkit.cite("key"))
```
Other unequal lengths raise a Polars `ComputeError` when the query executes.
## Handle row failures
Value and render expressions map null inputs, parse failures, missing citation keys, and rendering failures to null rows. `can_parse`, `diagnostics`, and `parse_report` expose parser outcomes:
```python
result = frame.select(
ok=pl.col("bibtex").refkit.can_parse(recovery="report"),
diagnostics=pl.col("bibtex").refkit.diagnostics(recovery="report"),
report=pl.col("bibtex").refkit.parse_report(recovery="report"),
)
```
`recovery="report"` keeps recoverable normalized entries and their parser diagnostics. The `parse_report` expression performs one parse for its complete struct result.
Static option errors are raised while the expression is constructed. Invalid input dtypes, unknown styles, unsupported projection fields, duplicate output names, and broadcasting failures raise when an eager query runs or a lazy plan collects.
## Inspect render failures
Use a key-list column and keep the report beside the source row:
```python
result = frame.select(
report=pl.col("bibtex").refkit.render_report(pl.concat_list("key")),
)
```
The report contains rendered citations, structured parser diagnostics, and an error code that distinguishes parse, missing-key, and render failures. Pass `grouped=True` to render the key list as one citation.
## Format rows
```python
result = frame.select(
bibtex=pl.col("bibtex").refkit.tidy_bibtex(options={"sort_fields": True, "wrap": 88}),
report=pl.col("bibtex").refkit.tidy_bibtex_report(options={"sort_fields": True}),
)
```
The report contains `ok`, `bibtex`, `count`, `warnings`, `renames`, and `error`. Read [Polars Expressions](/reference/polars) for exact dtypes, nullability, defaults, and empty-list behavior.
## Use lazy plans
Every operation returns `pl.Expr` and works in lazy plans:
```python
result = frame.lazy().select(pl.col("bibtex").refkit.entry_count().alias("entries")).collect()
```
Separate expressions parse independently. Name or alias repeated operations such as two `cite` expressions because their default output names are identical.
---
---
url: https://peter-gy.github.io/refkit/guides/browser.md
description: >-
Initialize RefKit in a browser or worker and serve the WebAssembly asset with
the required security policy.
---
# Run in a Browser
`refkit-js` loads RefKit's Rust core as [WebAssembly](https://webassembly.org/), a compiled module executed by the browser. Install the package with `npm install refkit-js`, then initialize it before using the [shared bibliography API](/get-started).
## Initialize the module
Use this example in an existing browser application built with a bundler such as [Vite](https://vite.dev/guide/). Put it in the application's TypeScript entry module, for example `src/main.ts`, and start the application with its development command, typically `npm run dev`. The bundler resolves the npm import and serves the compiled module.
Use a browser with WebAssembly and [FinalizationRegistry](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry), the JavaScript facility that releases native resources when objects become unreachable.
```ts
import * as rk from "refkit-js/browser";
await rk.init();
const library = rk.Library.parseBibtex(
"@book{doe2024, author={Doe, Jane}, title={Browser Citations}, year={2024}}",
);
console.log(rk.cite(library, "doe2024").text);
```
The browser console prints `(Doe, 2024)`. After initialization, parsing, rendering, and editing are synchronous. Concurrent `init()` calls share the loading operation, and later calls reuse the initialized module.
## Load on demand
Importing the browser entry loads the JavaScript API. The first `init()` loads the generated bindings and WebAssembly through a [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import). Put `init()` in the action that first needs bibliography processing:
```ts
import { init, Library, cite } from "refkit-js/browser";
export async function renderCitation(source: string, key: string): Promise {
await init();
return cite(Library.parseBibtex(source), key).text;
}
```
Call `renderCitation(source, key)` from an editor action or a file-selection handler. An application that needs RefKit immediately can await `init()` during startup. An application that can predict demand can call it earlier to preload the engine. Await the same initialization before processing input, and handle its rejection if loading fails. A failed WebAssembly download or compilation can be retried by calling `init()` again.
Keep [code splitting](https://vite.dev/guide/features.html#async-chunk-loading-optimization) enabled so the bundler preserves the deferred import. A build that combines dynamic imports into its entry file pays the embedded payload cost when that file loads. [Vite library builds](https://vite.dev/config/build-options.html#build-assetsinlinelimit) embed assets in JavaScript, so RefKit keeps its binary URL inside the deferred bindings. When publishing a library, keeping `refkit-js` external lets the consuming application own asset emission and caching.
## Serve the WebAssembly asset
The default loader resolves the packaged `.wasm` file relative to its JavaScript module. Bundlers such as [Vite](https://vite.dev/guide/assets.html#new-url-url-import-meta-url) can emit this asset and rewrite its URL. Keep the JavaScript and WebAssembly files from the same package version together.
For an explicit asset location, copy the file exported as `refkit-js/refkit.wasm` to your public assets and initialize with its URL:
```ts
import { init } from "refkit-js/browser";
await init(new URL("/assets/refkit.wasm", location.origin));
```
Serve it over HTTP or HTTPS with the [`application/wasm` media type](https://developer.mozilla.org/en-US/docs/WebAssembly/Reference/JavaScript_interface/instantiateStreaming_static). Cross-origin assets need an appropriate [Cross-Origin Resource Sharing response](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS) from the asset server.
If the application sets a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy/script-src), allow WebAssembly compilation with `'wasm-unsafe-eval'` in `script-src` and allow the asset origin in `connect-src`. A same-origin policy can include:
```text
script-src 'self' 'wasm-unsafe-eval'; connect-src 'self'
```
## Read user-supplied files
The [File API](https://developer.mozilla.org/en-US/docs/Web/API/File) reads a file selected by the user. Pass its text to the shared parser:
```ts
import { Library } from "refkit-js/browser";
async function readBibliography(file: File) {
return Library.parseBibtex(await file.text());
}
```
Call this function after initialization. `File.text()` decodes UTF-8. RefKit's Node [file helpers](/reference/javascript#node-filesystem-helpers) also handle Windows-1252-compatible bibliography files.
## Use a worker for longer operations
A [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers) runs work off the browser's main thread. Import and initialize RefKit inside each module worker, then return text or ordinary result records through `postMessage`:
```ts
import * as rk from "refkit-js/browser";
await rk.init();
self.onmessage = ({ data }: MessageEvent) => {
const library = rk.Library.parseBibtex(data);
self.postMessage(rk.fullBibliography(library));
};
```
Each worker has its own module and bibliography objects. Pass source text or result records between workers. Follow the shared guides for [parsing](/guides/parse-bibliographies), [rendering](/guides/render-citations), [editing](/guides/edit-bibtex), and [formatting](/guides/format-bibtex).
To run the Python binding inside a browser, use [Pyodide](/pyodide).
---
---
url: https://peter-gy.github.io/refkit/pyodide.md
description: >-
Install RefKit PyEmscripten wheels in the tested Pyodide and Polars runtime
set.
---
# Run in Pyodide
[Pyodide](https://pyodide.org/) runs Python and native Python packages compiled to WebAssembly in a browser or Node.js. RefKit publishes PyEmscripten wheels, the wheel format used by Python extensions compiled through Emscripten for this runtime.
Release tests execute the Pyodide command-line runtime with this compatibility set:
| Component | Tested value |
| --- | --- |
| Python | 3.14 |
| Pyodide xbuild environment | 314.0.2 |
| PyEmscripten platform | `pyemscripten_2026_0_wasm32` |
| Polars for `polars-refkit` | 1.33.1 |
The xbuild environment pins the compiler, runtime application binary interface, and build flags. It is a build-environment version rather than the Pyodide product version.
## Load `refkit`
Initialize a compatible runtime using the [Pyodide initialization guide](https://pyodide.org/en/stable/usage/quickstart.html). The examples assume its `pyodide` instance is ready. Install through [micropip](https://micropip.pyodide.org/):
```javascript
await pyodide.loadPackage("micropip")
await pyodide.runPythonAsync(`
import micropip
await micropip.install("refkit")
`)
```
Run the regular Python API inside that instance:
```python
import refkit as rk
library = rk.Library.parse_bibtex(
"""
@article{doe2024,
author = {Doe, Jane},
title = {Browser Citations},
year = {2024}
}
"""
)
document = rk.Document(library, rk.Style.load("apa"), locale="en-US")
rendered = document.render([rk.Citation("intro", "doe2024")])
print(rendered["intro"].text)
```
Expected output:
```text
(Doe, 2024)
```
Parsing, raw BibTeX editing, formatting, citation rendering, bibliography rendering, and structured output use the same Python API as CPython.
## Add Polars expressions
The Polars Python wheel and the `polars-refkit` native plugin must share a compatible plugin application binary interface.
```javascript
await pyodide.loadPackage("micropip")
await pyodide.runPythonAsync(`
import micropip
await micropip.install(["polars==1.33.1", "polars-refkit"])
`)
```
Then import the normal packages:
```python
import polars as pl
import polars_refkit
frame = pl.DataFrame(
{
"bibtex": ["@article{doe2024, author={Doe, Jane}, title={Browser Citations}, year={2024}}"],
"key": ["doe2024"],
}
)
row = frame.select(
count=pl.col("bibtex").refkit.entry_count(),
citation=pl.col("bibtex").refkit.cite("key"),
).to_dicts()[0]
assert row == {"count": 1, "citation": "(Doe, 2024)"}
```
A different Polars wheel can satisfy the Python version range and still fail native plugin loading. Keep the documented Polars and `polars-refkit` pair together.
## Use the Pyodide CLI
The Pyodide CLI creates a virtual environment that runs the PyEmscripten runtime. Install the pinned build tool, create the environment, and activate it before installing RefKit:
```bash
python -m pip install 'pyodide-build==0.35.1'
pyodide xbuildenv install 314.0.2
pyodide venv .venv-pyodide
. .venv-pyodide/bin/activate
python -m pip install refkit
python -m pip install 'polars==1.33.1' polars-refkit
```
An installation error that reports no compatible wheel means the package index has no wheel for the active PyEmscripten platform. Use the tested compatibility set or choose a RefKit release built for that runtime.
The [Pyodide package loading guide](https://pyodide.org/en/stable/usage/loading-packages.html) covers JavaScript initialization, package repositories, and `micropip` behavior.
---
---
url: https://peter-gy.github.io/refkit/troubleshooting.md
description: >-
Diagnose parsing, raw ambiguity, rendering, browser initialization, Polars
plugin, source-build, and Pyodide failures.
---
# Troubleshooting
Start with the boundary that produced the failure. Preserve the original bibliography source and gather the smallest diagnostic before changing it.
## Bibliography parsing fails
A `RefkitError` during file loading can mean the file could not be read or its extension is unsupported. Confirm the path and use `.bib`, `.yaml`, or `.yml` for normalized library loading. A `ParseError` carries diagnostics about the source itself.
Run a failing BibTeX parse with report recovery:
::: code-group
```python [Python]
import refkit as rk
source = "@book{doe2024, title={Example}, year={2024}}\n@book{broken"
library = rk.Library.parse_bibtex(source, recovery="report")
print(library.keys()) # ['doe2024']
print(library.diagnostics[0]["action"]) # dropped_block
```
```ts [TypeScript]
import * as rk from "refkit-js";
const source = "@book{doe2024, title={Example}, year={2024}}\n@book{broken";
const library = rk.Library.parseBibtex(source, { recovery: "report" });
console.log(library.keys()); // ["doe2024"]
console.log(library.diagnostics[0]?.action); // dropped_block
```
:::
If the source produces recoverable entries, fix the reported blocks and return to the default error policy. If it produces no entries, report recovery also raises `ParseError`. Inspect that exception's `diagnostics` or parse the source with `BibDocument.parse` to inspect its failed blocks through Python `failed_blocks` or TypeScript `failedBlocks`.
## A raw key is ambiguous
Use occurrence access to inspect duplicate entries and fields:
::: code-group
```python [Python]
raw = rk.BibDocument.parse("@book{duplicate, title={First}}\n@book{duplicate, title={Second}}")
entries = raw.entries.get_all("duplicate")
fields = entries[0].fields.get_all("title")
print(fields[0].value) # First
```
```ts [TypeScript]
const raw = rk.BibDocument.parse(
"@book{duplicate, title={First}}\n@book{duplicate, title={Second}}",
);
const entries = raw.entries.getAll("duplicate");
const fields = entries[0]!.fields.getAll("title");
console.log(fields[0]!.value); // First
```
:::
Choose the occurrence by source order or byte span before assigning a value. [Edit Raw BibTeX](/guides/edit-bibtex) explains occurrence identity and writeback.
## A citation key is missing
Inspect `library.keys()` before rendering. `Document.render` raises `MissingReferenceError` before returning partial citation output. Citation keys refer to the parsed library, so use final keys from the rename report after formatting has generated new keys.
## A style or locale fails
Use `Style.load(name)` and `Locale.load(code)` separately to validate bundled identifiers before creating a `Document`. For a custom [CSL](https://citationstyles.org/) (Citation Style Language) file, use Python `Style.from_path(path)` or Node `readStyle(path)` from `refkit-js/node`. In a browser, load XML text and pass it to `Style.fromXml`.
Dependent CSL styles require parent resolution and are rejected by the explicit-style constructors. Supply the independent parent style. [Errors and Diagnostics](/reference/errors#styles-and-rendering) lists style validation failures.
## Browser calls fail before initialization
Browser imports require `await init()` before parsing, formatting, or rendering. Follow [Initialize the module](/guides/browser#initialize-the-module) for the WebAssembly asset URL, server response, and content security policy requirements. Each worker initializes its own module.
## A Polars result is null
[Polars](https://docs.pola.rs/) is the Python dataframe integration. Use its report expressions to inspect row failures:
```python
import polars as pl
import polars_refkit # noqa: F401
frame = pl.DataFrame({"bibtex": ["@book{doe2024, title={Example}}"], "key": ["missing"]})
print(frame.select(pl.col("bibtex").refkit.parse_report(recovery="report")))
print(frame.select(pl.col("bibtex").refkit.render_report(pl.concat_list("key"))))
```
The parse report succeeds. The render report contains `error_code="missing_key"`. A successful parse with a null value-expression render result can also indicate null key input or a render failure. Use `render_report` to distinguish these outcomes.
## A Polars query raises `ColumnNotFoundError`
A string argument names a column. Wrap literal source and keys with `pl.lit`:
```python
print(frame.select(pl.lit("@book{doe2024, title={Example}}").refkit.cite(pl.lit("doe2024"))))
```
## A Polars query raises `DuplicateError`
Alias repeated expressions with the same default output name:
```python
print(
frame.select(
pl.col("bibtex").refkit.cite(pl.lit("doe2024")).alias("primary_citation"),
pl.col("bibtex").refkit.cite(pl.lit("doe2024")).alias("secondary_citation"),
)
)
```
## The Polars plugin will not load
Confirm that the installed Python package and native wheel come from one `polars-refkit` release. In [Pyodide](/pyodide), which runs Python in the browser through WebAssembly, use the documented Python, PyEmscripten, and Polars compatibility tuple. PyEmscripten identifies the Python build platform used by those wheels.
Reinstall both the package and matching Polars version in a clean environment when the application binary interface changed.
## Import reports a RefKit version mismatch
The `refkit` Python package checks the native extension version during import. Remove mixed editable and installed copies, then reinstall one complete release:
```bash
python -m pip install --force-reinstall refkit
```
## Pip starts a source build
Pip builds from the source distribution when no compatible wheel is available. Install a Rust toolchain, Git, and a working Python build environment, or choose a platform and Python version covered by the release wheels. Source builds fetch the pinned Hayagriva and Citationberg Git revisions from GitHub unless they are cached.
## Pyodide cannot find a compatible wheel
Use the compatibility set in [Run in Pyodide](/pyodide). A wheel built for another PyEmscripten platform or Polars plugin ABI cannot load in the current runtime.
---
---
url: https://peter-gy.github.io/refkit/reference/python.md
description: >-
Look up every public refkit object, function, result, helper, and runtime
metadata contract.
---
# Python API
The `refkit` package exports normalized bibliography objects, citation rendering objects, raw BibTeX views, formatting records, errors, helpers, and runtime metadata.
## Normalized bibliography
### `Library.read(path, *, recovery="error")`
Reads `.bib`, `.yaml`, or `.yml` and returns a `Library`. File extensions are matched case-insensitively. The recovery policy applies to `.bib` parsing.
### `Library.parse_bibtex(source, *, recovery="error")`
Parses BibTeX or BibLaTeX source in memory. `"error"` raises `ParseError` on parser diagnostics. `"report"` keeps recoverable entries and exposes structured records through `diagnostics`.
### `Library.parse_yaml(source)`
Parses a Hayagriva YAML bibliography in memory.
### `Library`
| Member | Contract |
| --- | --- |
| `diagnostics` | Returns a new list of `refkit.types.Diagnostic` dictionaries. |
| `keys()` | Returns citation keys in library order. |
| `values()` | Returns normalized `Entry` objects in library order. |
| `get(key)` | Returns one entry or `None`. |
| `get_many(keys)` | Returns entries in requested order. Missing keys raise `KeyError`. |
| `select(selector)` | Returns entries matched by a Hayagriva selector. |
| `project(fields=None, *, keys=None)` | Returns dictionaries for selected fields and keys. |
| `is_empty()` | Returns whether the library has no entries. |
| `len(library)` | Returns the entry count. |
| `bool(library)` | Returns whether the library has entries. |
| `key in library` | Tests key membership. |
| `library[key]` | Returns one entry. A missing key raises `KeyError`. |
`project()` defaults to `key`, `title`, `doi`, and `volume`. Read [Data Shapes](/reference/data-shapes) for projection fields.
### `Entry`
| Property | Type | Behavior |
| --- | --- | --- |
| `key` | `str` | Citation key. |
| `entry_type` | `str` | Normalized TitleCase entry type. |
| `title` | `str \| None` | Normalized title. |
| `date` | `str \| None` | Normalized date. |
| `parents` | `list[Entry]` | New list of normalized parent entries. |
| `volume` | `str \| None` | Own volume or the first parent volume. |
| `doi` | `str \| None` | Digital object identifier. |
## Styles and citations
### `Style.load(name)`
Loads a bundled independent CSL style. Lookup is case-insensitive. An unknown name raises `ValueError`.
### `Style.from_xml(xml)`
Prepares independent CSL XML. Invalid XML, dependent styles, and invalid macro graphs raise `ValueError`. `id` is `"xml"`.
### `Style.from_path(path)`
Reads strict UTF-8 CSL XML and prepares an independent style. `id` is the path string.
`Style.title` returns the style title. `Style.id` identifies how RefKit loaded the style and is not guaranteed to equal the CSL `` element.
### `Locale.load(code)`
Validates a bundled CSL locale code. `code` returns the validated code.
### `Cite(key, *, locator=None, label=None)`
Creates one cite item. A locator with no label uses the `page` label. A label with no locator has no rendering effect. Invalid labels raise `ValueError` when rendering runs.
### `CitationGroup(items)`
Creates one group from an iterable of citation-key strings and `Cite` objects. A plain string is not accepted as the iterable. An empty group raises `ValueError`.
`items` returns a new list of normalized `Cite` objects. `len(group)` returns its item count.
### `Citation(id, citation, *, note_number=None)`
Creates a named citation from a key string, `Cite`, or `CitationGroup`. `id` names the result inside one render call. `group` returns the normalized `CitationGroup`. `note_number` identifies the document note containing this occurrence and is returned by the property of the same name.
## Rendering
### `Document(library, style, *, locale=None)`
Stores a prepared `Library`, `Style`, and optional locale code. `locale` accepts a string, `Locale`, or `None`. Use `Locale.load` when construction-time validation is required because raw strings are passed to the renderer.
### `Document.render(citations)`
Renders an iterable of uniquely named `Citation` objects with fresh citation state. Returns `RenderedDocument`. Missing citation keys raise `MissingReferenceError`.
### `Document.cited_bibliography(citations)`
Renders the bibliography for one ordered iterable of uniquely named `Citation` objects. Returns `Rendered`. Duplicate IDs or locator labels raise `ValueError`. Missing keys raise `MissingReferenceError`. Renderer failures raise `RefkitError`.
### `Document.full_bibliography()`
Renders every entry in the library with fresh citation state. Returns `Rendered`.
### `RenderedDocument`
| Member | Contract |
| --- | --- |
| `citation_order` | New list of result IDs in input order. |
| `citations` | New dictionary from ID to `Rendered`. |
| `bibliography` | Cited bibliography as `Rendered`. |
| `rendered[id]` | Named citation. A missing ID raises `KeyError`. |
### `Rendered`
`text` returns plain text. `html` returns rendered HTML. `tree` returns fresh JSON-shaped Python data. `layout` returns a `BibliographyLayout` dictionary for bibliography output and `None` for a citation. Read [Data Shapes](/reference/data-shapes) for the tree and layout protocols.
## Raw BibTeX
### `BibDocument.read(path)` and `BibDocument.parse(source)`
Create a live raw BibTeX document from a file or string. Malformed blocks remain available through `failed_blocks`. The document and its live views must be used on the Python thread that created them.
| Member | Contract |
| --- | --- |
| `entries` | Live `BibEntryMap` view. |
| `diagnostics` | Source decode diagnostics as `Diagnostic` dictionaries. |
| `comments` | New source-order list of comment strings. |
| `preamble` | Preamble values joined with `#`. |
| `strings` | New dictionary of string definitions sorted by key. |
| `failed_blocks` | New list of malformed block records. |
| `blocks` | New list of every source-order block record. |
| `to_bibtex()` | Serializes the current in-memory state. |
| `tidy(*, options=None)` | Strictly formats the current state into `TidyResult`. |
| `write(path)` | Writes the current state as UTF-8. |
### `BibDocument.resolve()`
Returns `list[refkit.types.ResolvedBibEntry]` in source order. Each record has
`key`, `entry_type`, and a `fields` dictionary containing every source field
with string macros and concatenations expanded. Results are detached from the
document and reflect its current edits. Invalid or ambiguous input raises
`ParseError` with diagnostics. See [field resolution](/guides/edit-bibtex#resolve-fields-for-inspection).
### `BibEntryMap` and `BibFieldMap`
Both lengths count source occurrences, including duplicates. `unique_keys()` counts names through its returned list.
Both lookup views provide `unique_keys()`, `occurrence_keys()`, `occurrences()`, `get_all(key)`, `get_unique(key)`, `is_empty()`, length, truthiness, membership, and item lookup. They are focused views and do not implement the full Python `Mapping` interface.
Entry keys are case-sensitive. Field lookup is case-insensitive. A missing direct lookup raises `KeyError`. An ambiguous direct lookup raises `RefkitError` and should be replaced with `get_all`.
### `BibEntry`
`key` returns the raw citation key, `kind` returns the raw entry type spelling, `fields` returns a live `BibFieldMap`, and `span` returns a half-open byte span.
### `BibField`
`name`, `value`, and `span` describe one raw field occurrence. Assigning `value` mutates the owning `BibDocument` after delimiter validation.
## Formatting helpers
### `tidy_bibtex(source, *, options=None)`
Formats a BibTeX string and returns `TidyResult`.
### `tidy_file(path, *, output=None, options=None)`
Reads and formats a BibTeX file. When `output` is set, writes the formatted result to that path.
### `TidyResult` and `TidyWarning`
`TidyResult.bibtex` is formatted source. `count` is the input entry count, including entries merged from output. `warnings` is a list of `TidyWarning` records. `renames` is a source-order list of `TidyRename` dictionaries with `entry_id`, `old_key`, and `new_key`. Use it to update citation keys outside the bibliography.
A warning exposes `code`, optional duplicate `rule`, and `message`. Codes are `missing_key` and `duplicate_entry`.
Read [Tidy Options](/reference/tidy-options) for `TidyOptions`.
## Path Helpers
### `cite(source, citation, *, style="apa", locale="en-US")`
Reads a bibliography path and renders one citation. `citation` accepts a key string, `Cite`, or `CitationGroup`. `style` accepts a bundled name or prepared `Style`.
### `full_bibliography(source, *, style="apa", locale="en-US")`
Reads a bibliography path and renders every normalized entry.
## Typed records
Import dictionary and tree types from `refkit.types`:
```python
from refkit.types import Diagnostic, ProjectionRow, RenderedTree, TidyRename
```
These runtime `TypedDict` definitions describe records returned by RefKit. They can be inspected by type and schema tools. [Data Shapes](/reference/data-shapes) defines their keys, values, and nullability.
## Runtime metadata
`__version__` is the installed distribution version. `build_info` identifies the RefKit version, operating system, and architecture. `build_mode` is `"debug"` or `"release"`.
Import verifies that package metadata and the native extension have the same version. A mismatch raises `SystemError`. Reinstall one complete RefKit release to restore the pair.
---
---
url: https://peter-gy.github.io/refkit/reference/javascript.md
description: >-
Look up refkit-js initialization, bibliography objects, rendering, raw edits,
formatting, and Node file helpers.
---
# TypeScript API
`refkit-js` exports bibliography classes and typed result records for Node.js
and browsers. [Get Started](/get-started) covers installation and the first rendering.
[Run in a Browser](/guides/browser) covers browser assets and initialization. The package includes
[TypeScript](https://www.typescriptlang.org/) declarations for editor completion
and compile-time checking of every exported signature and record.
## Imports and initialization
| Import | Contract |
| --- | --- |
| `refkit-js` | Shared API and Node helpers under Node.js, with WebAssembly initialized during import. Browser resolution requires `await init()`. |
| `refkit-js/browser` | Shared API with bindings and WebAssembly deferred until `init()`. |
| `refkit-js/node` | Shared API and asynchronous filesystem helpers for Node.js. |
| `refkit-js/refkit.wasm` | Packaged WebAssembly asset for bundlers and deployment tooling. |
### `init(input?)`
Dynamically loads the generated bindings and packaged WebAssembly module and returns
a `Promise`. Importing the browser entry leaves this loading operation deferred. With no argument,
the browser loader resolves the packaged `.wasm` file relative to its module.
Pass a URL for a separately served asset. `InitOptions` also accepts a request,
response, byte buffer, compiled `WebAssembly.Module`, a promise for those inputs,
or a `{ module_or_path: input }` object. Concurrent calls share one promise and use
the first call's input. A failed WebAssembly download or compilation rejects the promise and
allows a subsequent call to retry.
Calls after successful initialization resolve immediately and retain the loaded module.
Await initialization before parsing, rendering, formatting, loading styles or locales,
or calling `getBuildInfo()`. Citation descriptions such as `Cite` and `Citation`
can be constructed before initialization. Read the [browser deployment requirements](/guides/browser#initialize-the-module)
for asset delivery and content security policy.
## Normalized bibliography
### `Library.parseBibtex(source, { recovery = "error" } = {})`
Parses [BibTeX](https://ctan.org/pkg/bibtex) or
[BibLaTeX](https://ctan.org/pkg/biblatex) reference text and returns a `Library`. `"error"` throws
`ParseError` on parser diagnostics. `"report"` retains recoverable entries and
exposes diagnostics. A source that cannot produce a recovered library still
throws `ParseError`.
### `Library.parseYaml(source)`
Parses a [Hayagriva YAML](https://github.com/typst/hayagriva) bibliography,
a structured reference format, and returns a `Library`.
### `Library`
| Member | Contract |
| --- | --- |
| `diagnostics` | Structured parser diagnostics. |
| `size` | Number of normalized entries. |
| `keys()` / `values()` | Keys or `Entry` records in library order. |
| `get(key)` | `Entry` or `null`. |
| `getMany(keys)` | Entries in requested order, including repeated keys. Throws for a missing key. |
| `has(key)` / `isEmpty()` | Membership or emptiness. |
| `select(selector)` | Entries matched by a [selector](/reference/selectors). |
| `project(fields?, { keys } = {})` | Records containing requested fields, optionally restricted to ordered keys. |
`project()` defaults to `key`, `title`, `doi`, and `volume`. Supported fields
are `key`, `entryType`, `type`, `title`, `date`, `doi`, and `volume`. `entryType`
and `type` select the normalized entry type and preserve the requested field
name. Missing selected keys throw. A missing field value is `null`.
`Entry` records expose `key`, `entryType`, `title`, `date`, `doi`, `volume`, and
`parents`. `entryType` uses normalized TitleCase names such as `Book`. `parents` is an array of `Entry` records. `title`, `date`, `doi`, and
`volume` can be `null`. `volume` uses the entry's own volume or its first
parent's volume. A `Library` is iterable over its `Entry` records.
## Styles and citation inputs
### `Style.load(name)` and `Style.fromXml(xml)`
Return a prepared [Citation Style Language](https://citationstyles.org/)
style, which controls citation and bibliography formatting. Bundled lookup is
case-insensitive. Custom XML must describe an independent style. Invalid XML,
invalid macro graphs, dependent styles, and unknown bundled names throw.
`title` is the style title. `id` is the requested bundled name or `"xml"` for
an XML input.
### `Locale.load(code)`
Validates a bundled locale and returns a `Locale` with its `code` property.
### `new Cite(key, { locator = null, label = null } = {})`
Creates a citation item. A locator with an omitted label uses `page`. A label
has a rendering effect when a locator is supplied. Invalid labels throw at
render time.
### `new CitationGroup(items)`
Creates a group from an iterable of key strings and `Cite` objects. Empty
groups and a bare string passed as `items` throw. `items` exposes the normalized
`Cite` array, and `size` gives its length.
### `new Citation(id, citation, { noteNumber = null } = {})`
Creates a named occurrence from a key string, `Cite`, or `CitationGroup`.
`id` identifies its result within one render call. `group` exposes the
normalized citation group. `noteNumber`, when supplied, is an integer from
1 through 4294967295 identifying the document note.
## Rendering
### `new Document(library, style, { locale = null } = {})`
Prepares a document with a library, style, and optional locale string or
`Locale`. Use `Locale.load(code)` for explicit locale validation. Each render
operation creates fresh citation state.
| Method | Result |
| --- | --- |
| `render(citations)` | `RenderedDocument` containing named citations and the cited bibliography. |
| `citedBibliography(citations)` | `Rendered` bibliography restricted to the ordered citation inputs. |
| `fullBibliography()` | `Rendered` bibliography containing every library entry. |
`citations` is an iterable of `Citation` objects with unique IDs. Missing
bibliography keys throw `MissingReferenceError`. Duplicate IDs and invalid
note numbers or locator labels throw.
### `RenderedDocument`
`citationOrder` is the input-order array of IDs. `citations` maps IDs to
`Rendered` records. `get(id)` returns a citation and throws for a missing ID.
`bibliography` is the cited bibliography.
### `Rendered`
`text` is plain text, `html` is rendered HTML, and `tree` is a typed node array.
`layout` contains bibliography spacing and alignment, or `null` for citation
output. Returned records are JavaScript data that can outlive their document.
The tree retains text formatting, links, source entry metadata, and separate
bibliography labels and content. It uses the same discriminants and enum
values as [Data shapes](/reference/data-shapes), with camelCase property names:
`itemIndex`, `citeIdx`, `fontStyle`, `fontVariant`, `fontWeight`,
`textDecoration`, `verticalAlign`, `hangingIndent`, `secondFieldAlign`,
`lineSpacing`, and `entrySpacing`. The exported `RenderedTree`, `RenderedNode`,
`RenderedMeta`, and `BibliographyLayout` types define each variant.
### `cite(library, citation, { style = "apa", locale = "en-US" } = {})`
Renders one key string, `Cite`, or `CitationGroup` and returns `Rendered`.
`style` accepts a bundled name or prepared `Style`.
### `fullBibliography(library, { style = "apa", locale = "en-US" } = {})`
Renders every entry and returns `Rendered`. Both convenience helpers accept
a `Library`. Node callers read paths with `readLibrary()` first.
## Raw BibTeX
### `BibDocument.parse(source)`
Creates a raw bibliography with live entry and field views. Malformed blocks
remain available through `failedBlocks`.
| Member | Contract |
| --- | --- |
| `entries` | Live `BibEntryMap`. |
| `diagnostics` | Source decoding diagnostics. |
| `comments` | Source-order comment strings. |
| `preamble` | Preamble values joined with `#`. |
| `strings` | String definitions keyed by name. |
| `failedBlocks` / `blocks` | Failed blocks or every source-order block. |
| `toBibtex()` | Serializes the current edits. |
| `resolve()` | Returns detached `ResolvedBibEntry` records with expanded source fields. |
| `tidy({ options } = {})` | Formats the current edits into a `TidyResult`. |
`BibEntry` exposes `key`, `kind`, `fields`, and `span`. `BibField` exposes
`name`, mutable `value`, and `span`. Assigning `value` validates the original
field delimiter before updating the document. `tidy()` returns a formatted
result and preserves the current raw document.
`resolve()` returns source-ordered records with `key`, `entryType`, and `fields`.
It uses the current field edits and raises `ParseError` for invalid or ambiguous
input. See [field resolution](/guides/edit-bibtex#resolve-fields-for-inspection)
for macro handling and diagnostics.
### `BibEntryMap` and `BibFieldMap`
| Member | Contract |
| --- | --- |
| `size` | Number of source occurrences, including duplicates. |
| `uniqueKeys()` | Distinct keys. |
| `occurrenceKeys()` / `occurrences()` | Keys or live views in source order. |
| `getAll(key)` | Every occurrence of a key. |
| `getUnique(key)` | One occurrence or `null`. Throws `RefkitError` for ambiguity. |
| `has(key)` / `isEmpty()` | Membership or emptiness. |
Entry keys are case-sensitive. Field lookup is case-insensitive. Both maps
are iterable over their source occurrences.
`span` is a half-open `[start, end]` pair of UTF-8 byte offsets into the original
source. JavaScript string slicing uses UTF-16 indexes, so convert offsets
before slicing non-ASCII text. Raw block records use the discriminants listed
in [Data shapes](/reference/data-shapes#raw-blocks-and-spans).
## Formatting
### `tidyBibtex(source, { options } = {})`
Formats a source string and returns `TidyResult`. `options` accepts a plain
object matching the exported `TidyOptions` interface.
`TidyResult` contains `bibtex`, `count`, `warnings`, and `renames`. `count` is
the input entry count, including merged entries. A rename contains `entryId`,
`oldKey`, and `newKey`. Warnings contain `code`, `rule`, and `message`. Their
codes are `missing_key` and `duplicate_entry`.
### `TidyOptions`
The shared [Tidy options](/reference/tidy-options) reference lists every
Python and TypeScript name, default, boolean shorthand, and key-template rule.
## Node filesystem helpers
Import these asynchronous functions from `refkit-js/node`. Paths accept a
string or file URL.
| Function | Contract |
| --- | --- |
| `readLibrary(path, { recovery = "error" } = {})` | Reads `.bib`, `.yaml`, or `.yml` into a `Library`. Extension matching is case-insensitive. |
| `readBibDocument(path)` | Reads a raw `BibDocument`. |
| `readStyle(path)` | Reads strict UTF-8 custom style XML into a `Style` whose `id` is the path string. |
| `tidyFile(path, { output, options } = {})` | Returns a `TidyResult`. Writes UTF-8 to `output` when supplied. |
Bibliography reads try UTF-8, then decode Windows-1252-compatible input and
attach a `text_encoding` diagnostic. Its spans refer to the decoded UTF-8
source. File errors reject the returned promise.
## Errors
[Errors and diagnostics](/reference/errors) defines exception classes,
argument validation, parser diagnostics, and formatting warnings for both bindings.
## Memory lifetime and version
JavaScript garbage collection manages RefKit objects and their WebAssembly
resources. A `Document` retains its library and style, and raw entry and field
views retain their owning bibliography. Returned entry and rendering records
are JavaScript data.
`version` is available during import. After initialization, `getBuildInfo()`
returns `{ version, buildMode, target }`. `buildMode` is `"debug"` or `"release"`,
and `target` identifies the WebAssembly compilation target. Keep the JavaScript
modules and WebAssembly asset from the same package version together when deploying.
---
---
url: https://peter-gy.github.io/refkit/reference/data-shapes.md
description: >-
Inspect typed diagnostics, projections, rendered trees, bibliography layout,
raw blocks, and Polars reports.
---
# Data Shapes
Python returns dictionaries described by [`TypedDict`](https://docs.python.org/3/library/typing.html#typing.TypedDict) definitions in `refkit.types`. TypeScript imports the corresponding object types from `refkit-js`.
::: code-group
```python [Python]
import refkit as rk
from refkit.types import ProjectionRow
library = rk.Library.parse_bibtex("@book{doe2024, title={Example}, year={2024}}")
rows: list[ProjectionRow] = library.project(["key", "entry_type"])
print(rows) # [{'key': 'doe2024', 'entry_type': 'Book'}]
```
```ts [TypeScript]
import * as rk from "refkit-js";
import type { ProjectionRow } from "refkit-js";
const library = rk.Library.parseBibtex("@book{doe2024, title={Example}, year={2024}}");
const rows: ProjectionRow[] = library.project(["key", "entryType"]);
console.log(rows); // [{ key: "doe2024", entryType: "Book" }]
```
:::
Tables use language-neutral `string`, `integer`, `boolean`, `list`, and `null`. Python represents null as `None`. Property names match across bindings unless separate columns show a mapping. Polars expresses these data as scalar, list, and struct columns, with the differences named beside each shape.
## Diagnostics
`Library.diagnostics`, `BibDocument.diagnostics`, and `ParseError.diagnostics` return a list of `Diagnostic` records:
| Field | Value | Meaning |
| --- | --- | --- |
| `code` | string | Machine-readable diagnostic category. |
| `severity` | `"error"` or `"warning"` | Severity of this diagnostic, independent of the overall operation result. |
| `action` | string | `rejected`, `dropped_block`, `dropped_field`, `literalized`, or `decoded`. |
| `span` | byte span or null | Half-open UTF-8 byte offsets into the original decoded source, when available. |
| `entry` | string or null | Affected citation key, when known. |
| `field` | string or null | Affected field, when known. |
| `message` | string | Human-readable explanation. |
A failed parse can contain warning diagnostics for recovery actions, such as dropped blocks, when no entries survive. Use the exception or report status to determine whether the operation succeeded.
Polars diagnostics use the same fields. `span` is a nullable `Struct[start: UInt64, end: UInt64]`. Recovery can change input length internally, but reported spans refer to the input supplied by the caller.
## Projection rows
`Library.project` and Polars `entries` accept these fields:
| Python field | TypeScript field | Value | Meaning |
| --- | --- | --- | --- |
| `key` | `key` | string | Citation key. |
| `entry_type` | `entryType` | string | Normalized entry type. |
| `type` | `type` | string | Entry type under this requested field name. |
| `title` | `title` | string or null | Normalized title. |
| `date` | `date` | string or null | Normalized date. |
| `doi` | `doi` | string or null | Digital object identifier. |
| `volume` | `volume` | string or null | Own volume or first parent volume. |
The default projection is `key`, `title`, `doi`, and `volume`. Both bindings return a list of `ProjectionRow` records. Each row contains exactly the requested fields, using the requested property names.
## Resolved entry records
`BibDocument.resolve()` returns detached records in source order:
| Field | Python | TypeScript |
| --- | --- | --- |
| Citation key | `key: str` | `key: string` |
| Lowercase source entry type | `entry_type: str` | `entryType: string` |
| Expanded source fields | `fields: dict[str, str]` | `fields: Readonly>` |
Field names are lowercase. String macros and concatenations are expanded while
literal TeX grouping and escapes are preserved. The fields include custom names
and source reference keys such as `crossref`.
Resolution errors use the same diagnostic shape. Their spans address the
current serialized document, including edits made before the call.
Polars `resolve` returns `List[Struct[key: String, entry_type: String,
fields: List[Struct[name: String, value: String]]]]`. The field list represents
the dictionary within Polars' fixed dtype system. Empty bibliographies return
an empty list. Null input or a resolution failure returns null.
## Rendered nodes
`Rendered.tree` contains a `RenderedTree` list. Each node has a case-sensitive `kind`:
| Kind | Fields |
| --- | --- |
| `Text` | `text: string`, `formatting: RenderedFormatting` |
| `Element` | `display: string \| null`, `meta: RenderedMeta \| null`, `children: list[RenderedNode]` |
| `Markup` | `value: string` |
| `Link` | `text: string`, `url: string`, `formatting: RenderedFormatting` |
| `Transparent` | Integer citation index (`cite_idx` in Python, `citeIdx` in TypeScript), `formatting: RenderedFormatting`. |
| `bibliography-entry` | `key: string`, `label: RenderedNode \| null`, `content: list[RenderedNode]` |
A bibliography label and content are separate. Render the label once, followed by content. `Transparent` retains citation-index metadata and produces no visible text. Treat `Markup.value` as text when creating HTML. RefKit's HTML renderer escapes it.
### Formatting and display
`RenderedFormatting` has five required fields:
| Python field | TypeScript field | Values |
| --- | --- | --- |
| `font_style` | `fontStyle` | `Normal`, `Italic` |
| `font_variant` | `fontVariant` | `Normal`, `SmallCaps` |
| `font_weight` | `fontWeight` | `Normal`, `Bold`, `Light` |
| `text_decoration` | `textDecoration` | `None`, `Underline` |
| `vertical_align` | `verticalAlign` | `None`, `Baseline`, `Sup`, `Sub` |
`Element.display` is `Block`, `LeftMargin`, `RightInline`, `Indent`, or null. A custom renderer maps these layout roles to elements and styles.
### Source metadata
`Element.meta` is null or a record identified by its `kind`:
| `kind` | Additional fields |
| --- | --- |
| `Entry` | `key: string`, integer item index (`item_index` in Python, `itemIndex` in TypeScript) |
| `Names` | `roles: list[string]` |
| `Name` | `role: string`, `index: integer` |
| `Date`, `Text`, `Number`, `Label`, `CitationNumber`, `CitationLabel` | None. |
The entry key identifies the bibliography record. Item and name indexes retain their positions in the rendered citation and name list.
### Bibliography layout
`Rendered.layout` is null for citation output. A bibliography returns `BibliographyLayout`:
| Python field | TypeScript field | Value |
| --- | --- | --- |
| `hanging_indent` | `hangingIndent` | boolean |
| `second_field_align` | `secondFieldAlign` | `Margin`, `Flush`, or null |
| `line_spacing` | `lineSpacing` | integer |
| `entry_spacing` | `entrySpacing` | integer |
Apply these values to the bibliography as a whole. Line spacing applies within entries and entry spacing applies between entries. [Render Structured Output](/guides/render-output) shows a complete tree consumer.
## Raw blocks and spans
`BibDocument.blocks` returns source-order `RawBlock` records. Every record contains `kind` and `span`:
| Kind | Additional fields |
| --- | --- |
| `whitespace` | None. |
| `comment` | `raw`. |
| `preamble` | `value`. |
| `string` | `key`, `value`. |
| `entry` | `id`, `key`. |
| `failed` | `raw`, `error`. |
| `other` | `raw`. |
A `RawSpan` is a two-item Python tuple or TypeScript array, including raw blocks, entries, fields, and diagnostic locations. They index UTF-8 bytes in the original decoded source and retain those positions after edits. Python `BibDocument.write` encodes the current text as UTF-8. JavaScript `BibDocument.toBibtex()` returns the current source string for the host application to write. For a file decoded from Windows-1252, these offsets differ from the original file-byte offsets.
## Tidy renames
`TidyResult.renames` contains source-order `TidyRename` records:
| Python field | TypeScript field | Value |
| --- | --- | --- |
| `entry_id` | `entryId` | integer source occurrence ID |
| `old_key` | `oldKey` | original key string |
| `new_key` | `newKey` | final key string |
The entry ID identifies a source occurrence. A merged entry can map to its retained entry's final key. Use occurrence identity when duplicate source keys make a key-only map ambiguous. RefKit updates bibliography `crossref` and `xdata` references. Use the rename records to update citations in other files.
## Polars reports
Each report expression returns null for a null source row.
### Parse report
```text
Struct[
ok: Boolean,
entry_count: UInt32,
keys: List[String],
diagnostics: List[Diagnostic]
]
```
A failed parse returns `ok=False`, null `entry_count` and `keys`, and diagnostics. Report recovery can return `ok=True` with diagnostics.
### Render report
```text
Struct[
ok: Boolean,
citations: List[Struct[text: String, html: String]],
diagnostics: List[Diagnostic],
error_code: String,
error: String
]
```
Successful output has null error fields. Failures use `parse_error`, `missing_key`, or `render_error` and an explanatory message. Null key-list input also produces a null report.
### Tidy report
```text
Struct[
ok: Boolean,
bibtex: String,
count: UInt32,
warnings: List[Struct[code: String, rule: String, message: String]],
renames: List[Struct[entry_id: UInt64, old_key: String, new_key: String]],
error: String
]
```
Successful output has formatted source, input entry count, warnings, rename records, and a null error. Failure has `ok=False` and an error message.
---
---
url: https://peter-gy.github.io/refkit/reference/tidy-options.md
description: >-
Look up every TidyOptions argument, default, merge strategy, duplicate rule,
and key-template behavior.
---
# Tidy Options
`TidyOptions` configures canonical BibTeX formatting. Python accepts keyword arguments. TypeScript accepts an options object.
::: code-group
```python [Python]
import refkit as rk
options = rk.TidyOptions(sort_fields=True, wrap=88)
result = rk.tidy_bibtex("@book{doe2024, title={Example}, year={2024}}", options=options)
print(result.count) # 1
```
```ts [TypeScript]
import * as rk from "refkit-js";
import type { TidyOptions } from "refkit-js";
const options: TidyOptions = { sortFields: true, wrap: 88 };
const result = rk.tidyBibtex("@book{doe2024, title={Example}, year={2024}}", { options });
console.log(result.count); // 1
```
:::
## Options
Defaults use `true`, `false`, and `null`, corresponding to Python's `True`, `False`, and `None`. String-list options accept Python iterables or TypeScript arrays.
| Python | TypeScript | Default | Behavior |
| --- | --- | --- | --- |
| `space` | `space` | `2` | Spaces used for field indentation. |
| `tab` | `tab` | `false` | Indent fields with a tab. |
| `align` | `align` | `14` | Align values at a column. `false` or `null` disables alignment. `true` uses 14. |
| `blank_lines` | `blankLines` | `false` | Insert a blank line between entries. |
| `trailing_commas` | `trailingCommas` | `false` | Add a comma after the final field. |
| `wrap` | `wrap` | `null` | Wrap long values. `true` uses 80 columns. An integer selects the width. |
| `sort` | `sort` | `null` | Sort entries. `true` sorts by key. A list supplies sort fields. Prefix a field with `-` for descending order. |
| `sort_fields` | `sortFields` | `null` | Sort fields. `true` uses the canonical order. A list supplies the order. |
| `omit` | `omit` | `null` | Omit the named fields from output. |
| `curly` | `curly` | `false` | Render non-month values with curly-brace delimiters. |
| `numeric` | `numeric` | `false` | Render positive, nonzero digit strings without delimiters. |
| `months` | `months` | `false` | Normalize month names to BibTeX abbreviations. |
| `strip_enclosing_braces` | `stripEnclosingBraces` | `false` | Remove one redundant brace pair around a complete value. |
| `drop_all_caps` | `dropAllCaps` | `false` | Title-case a value that contains no lowercase letters while preserving Roman numerals. |
| `escape` | `escape` | `true` | Escape supported Unicode and LaTeX-sensitive text in non-verbatim fields while preserving commands and math spans. |
| `encode_urls` | `encodeUrls` | `false` | Convert underscores in URL fields to `\\%5F`. |
| `remove_empty_fields` | `removeEmptyFields` | `false` | Drop fields whose value is empty. |
| `remove_duplicate_fields` | `removeDuplicateFields` | `true` | Keep the first field when a field name repeats in one entry. |
| `max_authors` | `maxAuthors` | `null` | Keep at most this many authors and append `and others` when truncated. |
| `lowercase` | `lowercase` | `true` | Lowercase entry types and field names. |
| `enclosing_braces` | `enclosingBraces` | `null` | Add protective braces inside selected fields. `true` selects `title`. |
| `remove_braces` | `removeBraces` | `null` | Remove protective braces inside selected fields. `true` selects `title`. |
| `strip_comments` | `stripComments` | `false` | Remove source comments from output. |
| `tidy_comments` | `tidyComments` | `true` | Normalize comment layout. |
| `generate_keys` | `generateKeys` | `null` | Generate citation keys. `true` uses the built-in template. A string supplies a template. |
| `duplicates` | `duplicates` | `null` | Report entries matched by selected duplicate rules. |
| `merge` | `merge` | `null` | Merge matched duplicate entries with the selected strategy. |
## Duplicates and merging
Duplicate rules are `doi`, `key`, `abstract`, and `citation`. When duplicate detection or merging is enabled, duplicate-key matches are included in the warnings and are never merged implicitly.
Merge strategies are:
| Strategy | Result |
| --- | --- |
| `first` | Keep the first matched entry. |
| `last` | Keep the last matched entry. |
| `combine` | Keep existing fields and add fields missing from the retained entry. |
| `overwrite` | Add missing fields and replace matching fields with values from the later entry. |
When `merge` is set and `duplicates` is omitted, RefKit matches DOI, citation, and abstract values for merging. It still reports duplicate keys without merging them.
`TidyResult.count` reports the number of input entries. Merging can produce fewer output entries while leaving `count` unchanged.
## Default output contract
Default formatting also normalizes page ranges, converts line endings to LF, and ends output with a newline.
Concatenated `#` expressions preserve their atoms and delimiter modes. Value transforms such as month normalization, escaping, brace changes, numeric output, author truncation, and wrapping do not rewrite those expressions.
## Key templates
Set Python `generate_keys=True` or TypeScript `generateKeys: true` to use:
```text
[auth:required:lower][year:required][veryshorttitle:lower][duplicateNumber]
```
| Marker | Result |
| --- | --- |
| `auth` | First author's surname. |
| `authEtAl` | First two surnames, followed by `EtAl` when there are more authors. |
| `authors` | Every author surname. |
| `authors2` | First two surnames, followed by `EtAl` when truncated. Replace `2` with the desired count. |
| `veryshorttitle` | First title word after removing common function words. |
| `shorttitle` | First three title words after removing common function words. |
| `title` | Capitalized title words. |
| `fulltitle` | All title words with their source capitalization. |
| `year` | Digits from the year field. |
| Uppercase field name, such as `DOI` | Words from the named field. |
| `duplicateLetter` | Letter suffix when multiple entries generate the same key. |
| `duplicateNumber` | Numeric suffix when multiple entries generate the same key. |
Markers use square brackets. Append modifiers with `:`, in execution order: `required`, `lower`, `upper`, and `capitalize`.
::: code-group
```python [Python]
key_options = rk.TidyOptions(generate_keys="[auth:lower][year:required]")
key_result = rk.tidy_bibtex(
"@book{old, author={Doe, Jane}, title={Example}, year={2024}}",
options=key_options,
)
print(key_result.renames[0]["new_key"]) # doe2024
```
```ts [TypeScript]
const keyOptions: TidyOptions = { generateKeys: "[auth:lower][year:required]" };
const keyResult = rk.tidyBibtex(
"@book{old, author={Doe, Jane}, title={Example}, year={2024}}",
{ options: keyOptions },
);
console.log(keyResult.renames[0]?.newKey); // doe2024
```
:::
For `author={Doe, Jane}` and `year={2024}`, this template produces `doe2024`. A duplicate receives a suffix. Missing required source data keeps the entry's original key. Literal text outside markers is retained subject to citation-key character validation.
The formatter assigns globally unique final keys, including entries that retain their original key. It updates `crossref` and `xdata` references, then sorts by the emitted keys when key sorting is enabled. A source reference that could identify multiple final entries, or a transformation that creates a reference cycle, raises `TidyError` before output is returned.
`TidyResult.renames` records the source occurrence, old key, and final key for changed identities, including entries merged into another entry. Update citation keys in external documents from this report.
Malformed source raises `TidySyntaxError`. Invalid option types raise `TypeError`. Invalid duplicate rules and merge strategies raise Python `ValueError` or JavaScript `RangeError`. Invalid key templates raise `TidyError`. See [Errors and Diagnostics](/reference/errors) for location properties and warnings.
---
---
url: https://peter-gy.github.io/refkit/reference/polars.md
description: >-
Look up bibliography expressions, output formats, dtypes, broadcasting, and
row reports.
---
# Polars Expressions
Importing `polars_refkit` registers the `pl.Expr.refkit` namespace. Every expression also has a top-level builder with the bibliography column as its first argument.
## Shared inputs and options
`bibtex_col`, `key_col`, and `keys_col` accept a column name or `pl.Expr`. Use `pl.lit(...)` for literal bibliography text or keys.
Parsing accepts `recovery="error"` or `recovery="report"`. Rendering also accepts `style="apa"` and `locale="en-US"`. `style` selects a bundled style case-insensitively. `locale` is forwarded to the renderer, and an empty string selects no explicit locale.
Rendering accepts `output="text"`, `"html"`, or `"rendered"`. The output choice is validated when constructing the expression and determines its dtype. A rendered value is `Struct[text: String, html: String]`.
## Parse and inspect
| Expression | Output dtype | Parse failure |
| --- | --- | --- |
| `entry_count(bibtex_col, *, recovery="error")` | `UInt32` | Null. |
| `can_parse(bibtex_col, *, recovery="error")` | `Boolean` | `False`. |
| `has_diagnostics(bibtex_col, *, recovery="error")` | `Boolean` | Reflects diagnostic presence. |
| `keys(bibtex_col, *, recovery="error")` | `List[String]` | Null. |
| `entries(bibtex_col, *, fields=None, recovery="error")` | `List[Struct]` | Null. |
| `resolve(bibtex_col)` | `List[ResolvedBibEntry]` | Null. |
| `diagnostics(bibtex_col, *, recovery="error")` | `List[Diagnostic]` | Structured diagnostic records. |
| `parse_report(bibtex_col, *, recovery="error")` | Parse report struct | `ok=False` with diagnostics. |
`entries` defaults to `key`, `title`, `doi`, and `volume`. Supported fields are `key`, `entry_type`, `type`, `title`, `date`, `doi`, and `volume`. An empty field list returns one empty struct per entry. Unknown or repeated fields abort the query.
`parse_report` performs one parse for `ok`, `entry_count`, `keys`, and `diagnostics`. Null source produces a null report. [Data Shapes](/reference/data-shapes) defines report and diagnostic fields.
`resolve` expands string macros and concatenations across every source field,
including custom fields. It preserves literal TeX grouping and escapes. Each
row is independent, and an empty bibliography produces an empty list. See
[resolved entry records](/reference/data-shapes#resolved-entry-records) for the
fixed nested dtype. Use `BibDocument.resolve()` in Python to inspect a failed
row's structured `ParseError` diagnostics.
## Render citations
```python
cite(bibtex_col, key_col, *, style="apa", locale="en-US", recovery="error", output="text")
cite_each(bibtex_col, keys_col, *, style="apa", locale="en-US", recovery="error", output="text")
cite_group(bibtex_col, keys_col, *, style="apa", locale="en-US", recovery="error", output="text")
```
| Operation | Key dtype | Result per row |
| --- | --- | --- |
| `cite` | `String` | One citation. |
| `cite_each` | `List[String]` | A list of citations in input order, sharing citation state within the row. |
| `cite_group` | `List[String]` | One citation containing the ordered group. |
Text and HTML outputs are strings. `cite_each` returns a list of the selected output type. An empty `cite_each` key list returns `[]`. A group must contain at least one key. An empty `cite_group` produces null, and its grouped render report records a `render_error`.
## Render every entry
```python
full_bibliography(bibtex_col, *, style="apa", locale="en-US", recovery="error", output="text")
```
Renders the complete normalized library in each source row. Each row owns independent citation state.
## Inspect a render failure
```python
render_report(bibtex_col, keys_col, *, grouped=False, style="apa", locale="en-US", recovery="error")
```
Accepts a `List[String]` key column and returns `ok`, `citations`, `diagnostics`, `error_code`, and `error`. `grouped=False` renders one ordered citation per key. `grouped=True` renders one grouped citation. Each citation contains text and HTML.
`error_code` is `parse_error`, `missing_key`, or `render_error` on failure. Successful reports have null error fields. Null source or key-list input produces a null report.
## Format BibTeX
```python
tidy_bibtex(bibtex_col, *, options=None)
tidy_bibtex_report(bibtex_col, *, options=None)
```
Pass a dictionary typed as `polars_refkit.TidyOptions`:
```python
import polars_refkit as prk
options: prk.TidyOptions = {"sort_fields": True, "wrap": 88}
formatted = pl.col("bibtex").refkit.tidy_bibtex(options=options)
```
Omitted keys use the core defaults in [Tidy Options](/reference/tidy-options). Optional rules accept their explicit value or the documented boolean shorthand. `None` and `False` disable optional rules.
`tidy_bibtex` returns a string. `tidy_bibtex_report` returns `ok`, `bibtex`, `count`, `warnings`, `renames`, and `error`. Null input produces null output.
## Broadcasting and failures
Two-input expressions accept equal input lengths or a length-one input on either side. A singleton source is parsed once within that expression, including failed parses. Other lengths raise `ComputeError`. Separate expressions parse independently.
Value expressions return null for row-local null input, parse failure, missing citation keys, null key-list items, or render failure. Reports preserve the corresponding detail.
Invalid input dtypes, unknown styles, unsupported or repeated projection fields, and incompatible lengths abort the query. Invalid recovery, output, and tidy options raise during expression construction.
Each expression's default output name is its operation name. Alias repeated operations:
```python
frame.select(
primary=pl.col("bibtex").refkit.cite("key"),
html=pl.col("bibtex").refkit.cite("key", output="html"),
)
```
---
---
url: https://peter-gy.github.io/refkit/reference/selectors.md
description: >-
Filter normalized Library entries by type, fields, alternatives, and parent
structure.
---
# Selectors
`Library.select(selector)` filters normalized entries with the [Hayagriva selector language](https://github.com/typst/hayagriva/blob/main/docs/selectors.md), a grammar for matching entry types, fields, and parent structure.
::: code-group
```python [Python]
import refkit as rk
library = rk.Library.parse_bibtex("@book{doe2024, title={Example}, year={2024}}")
selected = library.select("book[title,date]")
print([entry.key for entry in selected]) # ['doe2024']
```
```ts [TypeScript]
import * as rk from "refkit-js";
const library = rk.Library.parseBibtex("@book{doe2024, title={Example}, year={2024}}");
const selected = library.select("book[title,date]");
console.log(selected.map((entry) => entry.key)); // ["doe2024"]
```
:::
## Grammar
| Selector | Matches |
| --- | --- |
| `article` | Entries of the named type. |
| `*` | Every entry. |
| `article[date]` | Articles with a date. |
| `article[author,title,date]` | Articles with a value for every listed field. |
| `article > periodical[volume]` | Articles with a periodical parent that has a volume. |
| `article > (conference & video)` | Articles with both matching parent types. |
| `book \| article` | Either entry type. |
| `!book` | Entries that fail the book selector. |
String selectors are case-insensitive. Entry types and fields use Hayagriva's normalized bibliography model. For example, a BibTeX `year` becomes part of `date`.
`>` requires a matching parent and can be chained for deeper relationships. `&` requires multiple matching parents on the right side of `>`. `|` selects alternatives, `!` negates the next selector, and parentheses group expressions.
`select` returns the top-level `Entry` objects that match. Selector bindings participate in matching. The returned records are the matching entries.
An invalid selector raises Python `ValueError` or JavaScript `RangeError` before any result list is returned.
---
---
url: https://peter-gy.github.io/refkit/reference/rust.md
description: >-
Inspect the source-workspace Rust API that supplies RefKit's portable
bibliography capabilities.
---
# Rust Core
The `refkit-core` crate is RefKit's portable, adapter-facing Rust API inside the source workspace. It accepts in-memory values and returns RefKit-owned records. Host paths, Python and JavaScript objects, and Polars values stay in their adapters.
Use the crate from a matching RefKit source or Git revision with Rust 1.88 or newer. The versioned bindings install through the `refkit` and `polars-refkit` Python distributions and the `refkit-js` npm package.
## Public capability groups
| Capability | Main exports |
| --- | --- |
| Normalized parsing | `Library`, `RecoveryPolicy`, `Diagnostic`, `ParseFailure`, `ParseReport`, `EntryRecord`, `EntryField`, `parse_bibtex_report` |
| Raw BibTeX | `RawDocument`, `ResolvedBibEntry`, occurrence IDs, inspection records, and edit errors |
| Rendering | `Document`, `Cite`, `CitationRequest`, `RenderedDocument`, `RenderedOutput`, render functions, and render errors |
| Styles | `PreparedStyle`, `load_prepared_style`, `prepare_style_from_xml`, and `StyleError` |
| Render tree | `RenderedRecord`, `RenderedNode`, `RenderedFormatting`, and `BibliographyLayout` |
| Formatting | `TidyOptions`, `TidyResult`, `TidyWarning`, `TidyRename`, duplicate rules, merge strategies, and `tidy_bibtex` |
| Decoding | `DecodedText`, `TextEncoding`, and `decode_bibliography` |
## Resolve source fields
`RawDocument::resolve(&self) -> Result, ParseFailure>`
expands string macros and concatenations in current source fields. Each record
contains `key`, `entry_type`, and a `BTreeMap` of `fields`.
The method preserves TeX text, custom fields, and entry order and leaves the
document unchanged. See [field resolution](/guides/edit-bibtex#resolve-fields-for-inspection)
for macro lookup and diagnostic semantics.
## Adapter boundary
The core API owns bibliography semantics and typed records. An adapter owns:
* Filesystem reads and writes.
* Host-specific objects and error classes.
* Serialization into dictionaries, structs, or other host values.
* Runtime registration and lifecycle.
The Python, JavaScript, and Polars adapters depend on this API. A new adapter should preserve the same capability meanings while choosing host-native inputs, outputs, and failure behavior.
## State model
Core `Library` owns normalized entries and parser diagnostics. Core `RawDocument` owns raw syntax, occurrences, and edits. Core `Document` stores prepared rendering inputs. Each render or bibliography call creates fresh processor state.
The crate serves the workspace adapter boundary. Adapter authors should pin the complete RefKit revision so the record and capability versions remain aligned.
See [How RefKit Works](/concepts/how-refkit-works) for the product model and the repository's `development_docs/` for contributor architecture.
---
---
url: https://peter-gy.github.io/refkit/reference/agent-docs.md
description: >-
Discover installed RefKit tasks, execute version-matched examples, and
retrieve focused agent documentation.
---
# Use RefKit with Agents
The Python and Polars packages each include an [Agent Plugin](https://agent-plugins.org/) with a version-matched [Agent Skill](https://agentskills.io/specification). An agent that can execute Python can discover the installed tasks:
```python
import refkit.agent
help(refkit.agent)
```
For a Polars environment:
```python
import polars_refkit.agent
help(polars_refkit.agent)
```
Module help identifies the installed version, summarizes the public API, and lists the packaged resources. Use those resources as the contract for the installed package. Website links describe the current documentation and can differ from an older installation.
## Choose a task
| Task | Python resource | Polars resource |
| --- | --- | --- |
| Inspect entries and recovery diagnostics | `references/inspect.md` | `references/inspect.md` |
| Render ordered citations or full bibliographies | `references/render.md` | `references/render.md` |
| Edit a raw BibTeX occurrence | `references/edit.md` | Use the Python object package. |
| Format, deduplicate, and generate keys | `references/tidy.md` | `references/tidy.md` |
| Look up object contracts | `references/contracts.md` | Use the installed expression signatures. |
Each task resource contains an independently executable example with concrete inputs and assertions. Start with the resource for the requested task, then inspect exact signatures or types when the workflow needs customization.
## Read the installed resources
```python
import refkit.agent as agent
instructions = agent.instructions()
resources = agent.resources()
inspection = resources["references/inspect.md"].read_text()
```
Both agent modules provide:
| Function | Result |
| --- | --- |
| `instructions()` | Skill instructions as Markdown. |
| `resources()` | Dictionary from skill-relative name to absolute `Path`. |
| `agent_plugin()` | The `agent_plugins.Plugin` handle. |
| `agent_skill()` | The `agent_plugins.Skill` handle. |
Resource functions raise `AgentPluginError` when the installation cannot supply its declared resources. Module help remains available and includes the recovery action. Reinstall the corresponding package when its resource payload is damaged.
## Discover capabilities in marimo
[Marimo](https://marimo.io/) discovers capability modules through package entry-point metadata. Install the relevant package in marimo's Python environment, then inspect discovery:
```python
import marimo._code_mode as cm
capabilities = cm.capabilities()
assert capabilities["refkit"] == "refkit.agent"
assert capabilities["polars_refkit"] == "polars_refkit.agent"
```
This example assumes both packages are installed. Each package contributes its own capability independently. Marimo's discovery API currently lives in the internal preview `marimo._code_mode` module. Agents can import the public capability modules directly in other Python execution environments.
## Fetch current documentation
RefKit publishes plain-text documentation using the [llms.txt convention](https://llmstxt.org/):
| Source | Task |
| --- | --- |
| [`llms.txt`](https://peter-gy.github.io/refkit/llms.txt) | Discover page descriptions and Markdown links. |
| [`llms-full.txt`](https://peter-gy.github.io/refkit/llms-full.txt) | Read the complete current public documentation. |
| A documentation route ending in `.md` | Read one focused page. |
For example:
```text
https://peter-gy.github.io/refkit/reference/python.md
https://peter-gy.github.io/refkit/guides/polars.md
```
The site and text views share authored Markdown, navigation, and deployment checks. Prefer installed skill resources for version-specific execution and focused website pages for current concepts or reference.
---
---
url: https://peter-gy.github.io/refkit/reference/errors.md
description: >-
Distinguish exceptions, parser diagnostics, tidy warnings, and Polars row or
query failures.
---
# Errors and Diagnostics
RefKit separates failed operations, parser diagnostics, and successful formatting warnings. Catch `ParseError` to inspect a failed parse's diagnostics in either binding.
::: code-group
```python [Python]
import refkit as rk
try:
rk.Library.parse_bibtex("@book{broken")
except rk.ParseError as error:
print(error.diagnostics[0]["code"])
```
```ts [TypeScript]
import * as rk from "refkit-js";
try {
rk.Library.parseBibtex("@book{broken");
} catch (error) {
if (!(error instanceof rk.ParseError)) throw error;
console.log(error.diagnostics[0]?.code);
}
```
:::
Both examples report `syntax_error`. Diagnostic codes identify the failure category. Messages describe the affected input.
## Error hierarchy
The RefKit hierarchy is shared. `RefkitError` derives from Python `Exception` or JavaScript `Error`.
```text
RefkitError
├── ParseError
├── MissingReferenceError
└── TidyError
└── TidySyntaxError
```
Argument validation also uses built-in exceptions. Python uses `TypeError` for invalid types and `ValueError` for invalid values. JavaScript uses `TypeError` and `RangeError`, respectively. Lookup methods have the specific behavior in the tables.
## Normalized parsing and lookup
| Operation | Python | TypeScript |
| --- | --- | --- |
| Read a bibliography file | `Library.read`: `RefkitError` for file reads or unsupported extensions, `ParseError` for parser failure. | `readLibrary` from `refkit-js/node`: same RefKit exceptions. |
| Parse BibTeX | `Library.parse_bibtex`: `ParseError` when the recovery policy cannot produce a library. | `Library.parseBibtex`: same failure. |
| Parse [Hayagriva YAML](https://github.com/typst/hayagriva/blob/main/docs/file-format.md), a structured bibliography format | `Library.parse_yaml`: `ParseError` for invalid source. | `Library.parseYaml`: same failure. |
| Look up one citation key | `Library[key]` raises `KeyError`. `Library.get` returns `None` when missing. | `Library.get` returns `null` when missing. |
| Look up several keys | `Library.get_many` raises `KeyError` for a missing key. | `Library.getMany` raises `MissingReferenceError`. |
| Select entries | `Library.select` raises `ValueError` for invalid syntax. | `Library.select` raises `RangeError`. |
| Project entries | `Library.project` raises `TypeError` for invalid collection arguments, `ValueError` for an unknown field, `KeyError` for an absent requested key. | `Library.project` raises `TypeError`, `RangeError`, or `MissingReferenceError`, respectively. |
Report recovery keeps recoverable entries and `Diagnostic` records. Each diagnostic includes a code, severity, recovery action, optional source span, entry key, field, and message. `ParseError.diagnostics` exposes the same shape when parsing fails. YAML failures include `yaml_parse_error` or `resource_limit` diagnostics. See [Data Shapes](/reference/data-shapes).
## Styles and rendering
[CSL](https://citationstyles.org/) (Citation Style Language) defines citation and bibliography formatting in XML style files.
| Failure | Python | TypeScript |
| --- | --- | --- |
| Unknown bundled style or locale | `Style.load` and `Locale.load` raise `ValueError`. | Same methods raise `RangeError`. |
| Invalid or dependent CSL XML, missing/duplicate/cyclic macros, or excessive macro expansion | `Style.from_xml` raises `ValueError`. | `Style.fromXml` raises `RangeError`. |
| Style file read failure | `Style.from_path` raises `RefkitError`. Invalid XML raises `ValueError`. | `readStyle` from `refkit-js/node` raises `RefkitError`. Invalid XML raises `RangeError`. |
| Invalid citation items or an empty group | `CitationGroup` raises `TypeError` for invalid items, `ValueError` for an empty group. | `CitationGroup` raises `TypeError` or `RangeError`, respectively. |
| Unnamed or invalid citation input | `Document.render` and `Document.cited_bibliography` raise `TypeError`. | `Document.render` and `Document.citedBibliography` raise `TypeError`. |
| Duplicate citation IDs or invalid locator labels | Render methods raise `ValueError`. | Render methods raise `RangeError`. |
| Missing citation key or renderer failure | Render methods raise `MissingReferenceError` or `RefkitError`, respectively. | Same exceptions. |
| Unknown result ID | `RenderedDocument[id]` raises `KeyError`. | `RenderedDocument.get(id)` raises `MissingReferenceError`. |
## Raw BibTeX
| Failure | Python | TypeScript |
| --- | --- | --- |
| Missing raw entry or field | Direct indexing raises `KeyError`. `get_unique` returns `None`. | `getUnique` returns `null`. |
| Ambiguous raw entry or field | Direct indexing and `get_unique` raise `RefkitError`. | `getUnique` raises `RefkitError`. |
| Unsafe field replacement | Assigning `BibField.value` raises `ValueError`. | Assigning `BibField.value` raises `RangeError`. |
| Malformed raw block during formatting | `BibDocument.tidy` raises `TidySyntaxError`. | Same exception. |
Python `BibDocument.write` raises `RefkitError` when the destination cannot be written. JavaScript `BibDocument.toBibtex()` returns a string for the host application's file API. Node `tidyFile` wraps destination write failures in `RefkitError`.
## Formatting
`TidySyntaxError` exposes these properties in both bindings:
| Property | Meaning |
| --- | --- |
| `line` | One-based line number. |
| `column` | One-based character column. |
| `byte` | Zero-based UTF-8 byte offset. |
| `character` | First character of the failing block, or `None` / `null` when unavailable. |
| `message` | Parser message without the location prefix. |
`TidyError` also covers key-template, name-processing, ambiguous reference-rewrite, and cyclic reference-rewrite failures. A successful `TidyResult` can contain structured `TidyWarning` values for missing keys and duplicate entries. [Tidy Options](/reference/tidy-options) lists the accepted options and defaults.
## Polars failures
Value expressions turn row-local input, parse, missing-key, and render failures into null. Report expressions preserve parser, renderer, or formatter details in structs.
Invalid static options can raise during expression construction. Invalid dtypes, projection fields, styles, output-name collisions, and broadcasting lengths raise from Polars when the query executes.
Read [Troubleshooting](/troubleshooting) for recovery steps for common failures.