# AUTO-GENERATED -- DO NOT EDIT.
#
# This file is regenerated by `scripts/regen-bindings.sh` from the
# JSON Schema at `crates/rustledger-wasm/bindings/index.schema.json`,
# which in turn comes from the Rust DTOs in
# `crates/rustledger-wasm/src/types.rs`. CI fails if this file drifts.
# See ADR-0004 (#1218 + #1232 for Phase 3) for the design.
from __future__ import annotations
from typing import Literal
from pydantic import BaseModel, Field, RootModel
class AmountValue(BaseModel):
"""
Amount value for serialization.
"""
currency: str = Field(..., description="The currency.")
number: str = Field(..., description="The number as a string.")
class CellValue1(BaseModel):
"""
Amount with number and currency.
"""
currency: str
number: str
class CompletionJson(BaseModel):
"""
BQL completion suggestion for WASM.
"""
category: str = Field(
..., description="Category: keyword, function, column, operator, literal."
)
description: str | None = Field(
None, description="Optional description/documentation."
)
text: str = Field(..., description="The completion text to insert.")
class CompletionKind(RootModel[str]):
root: str = Field(..., description="The kind of a completion item.")
class CompletionResultJson(BaseModel):
"""
Result of BQL completion request.
"""
completions: list[CompletionJson] = Field(..., description="List of completions.")
context: str = Field(..., description="Current context for debugging.")
class CostNumberJson1(BaseModel):
"""
Per-unit cost (e.g., `{100 USD}`).
"""
kind: Literal["per_unit"]
value: str = Field(..., description="Per-unit value.")
class CostNumberJson2(BaseModel):
"""
Total cost as written (e.g., `{{1000 USD}}`), pre-booking.
"""
kind: Literal["total"]
value: str = Field(..., description="Total value.")
class CostNumberJson3(BaseModel):
"""
Post-booking derived per-unit with preserved source total.
"""
kind: Literal["per_unit_from_total"]
per_unit: str = Field(..., description="Derived per-unit.")
total: str = Field(..., description="Source total.")
class CostNumberJson(RootModel[CostNumberJson1 | CostNumberJson2 | CostNumberJson3]):
root: CostNumberJson1 | CostNumberJson2 | CostNumberJson3 = Field(
...,
description="Wire-format of the numeric component of a [`PostingCostJson`].\n\nMirrors `rustledger_core::CostNumber` on the wire so JS consumers\nsee the same mutual exclusion the host enforces. Use the `kind`\nfield as the discriminator.",
)
class CostValue(BaseModel):
"""
Cost value for serialization.
"""
currency: str = Field(..., description="Cost currency.")
date: str | None = Field(None, description="Acquisition date.")
label: str | None = Field(None, description="Lot label.")
number: str = Field(..., description="Cost per unit.")
class EditorCompletion(BaseModel):
"""
A completion item for Beancount source editing.
"""
detail: str | None = Field(
None, description="A human-readable string with additional information."
)
insert_text: str | None = Field(
None, description="The text to insert when this completion is selected."
)
kind: CompletionKind = Field(..., description="The kind of completion item.")
label: str = Field(..., description="The label to display in the completion list.")
class EditorCompletionResult(BaseModel):
"""
Result of a completion request.
"""
completions: list[EditorCompletion] = Field(..., description="The completions.")
context: str = Field(..., description="The detected context.")
class EditorLocation(BaseModel):
"""
A location in the document.
"""
character: int = Field(..., description="Character offset (0-based).", ge=0)
line: int = Field(..., description="Line number (0-based).", ge=0)
class EditorRange(BaseModel):
"""
A range in the document.
"""
end_character: int = Field(..., description="End character (0-based).", ge=0)
end_line: int = Field(..., description="End line (0-based).", ge=0)
start_character: int = Field(..., description="Start character (0-based).", ge=0)
start_line: int = Field(..., description="Start line (0-based).", ge=0)
class LedgerOptions(BaseModel):
"""
Ledger options.
"""
operating_currencies: list[str] = Field(..., description="Operating currencies.")
title: str | None = Field(
...,
description="Ledger title. Emitted as JSON `null` when no title is set\n(no `skip_serializing_if`; field is always present on the\nwire). TS: `string | null`, not `title?`. The required-and-\nnullable wire contract is enforced via the `schemars(extend)`\non the struct itself; see `ParseResult` for the rationale.",
)
class MetaValueJson1(BaseModel):
"""
Amount values (`{number, currency}`) — the only structured
shape that survives the round-trip. Same `{number, currency}`
envelope as [`AmountValue`] so JS consumers can branch on
shape without a discriminator tag.
**Deserialize note**: serde's untagged-enum matcher accepts
extra fields in a JSON object (`#[serde(deny_unknown_fields)]`
can't be applied per-variant on an untagged enum without
breaking the wider match). A JS client sending
`{number: "100", currency: "USD", extra: "x"}` deserializes as
`Amount { number: "100", currency: "USD" }` with `extra`
silently dropped. Output-side consumers (the production path)
are unaffected; treat `Deserialize` here as best-effort and
validate at the host boundary if you need stricter checks.
"""
currency: str = Field(..., description="The currency code.")
number: str = Field(
..., description="The decimal quantity, stringified for precision."
)
class MetaValueJson(RootModel[str | bool | MetaValueJson1 | None]):
root: str | bool | MetaValueJson1 | None = Field(
...,
description="Metadata-value wire format for WASM consumers.\n\n**JSON output is byte-equivalent to FFI-WASI's\n`meta_value_to_json`** — JS clients writing portable code see\nidentical metadata values from both bindings. The Rust-side\ntypes are independent though: FFI-WASI emits\n`serde_json::Value` (untyped), this crate emits a typed enum.\nUnifying the source-of-truth is tracked by issue #1200 item 2.\n\nThe host's [`rustledger_core::MetaValue`] is richer than the wire\ntype — `Account`/`Currency`/`Tag`/`Link`/`Date`/`Number` all\nflatten to JSON strings here, matching FFI-WASI behavior. JS\nconsumers that need the strong type info should query the host\nvia a typed API; this enum is the lossy-but-portable view.\n\nUntagged on the wire: `\"hello\"` serializes as a string,\n`true` as a boolean, `null` as null, and an [`AmountValue`]\n`{number,currency}` as a plain object. The TypeScript union is\n`Record<string, string | boolean | {number, currency} | null>` —\nno raw JSON number arm because `MetaValue::Number` (`Decimal`)\nstringifies to preserve precision. Issue #1168 proposed\n`string | number | boolean | null`; we substitute the\n`{number,currency}` shape for `number` so cost-bearing metadata\nround-trips cleanly and so JS numeric literals don't silently\nalias into the wire (see the `meta_value_json_rejects_raw_json_number`\ntest).",
)
class PluginInfo(BaseModel):
"""
Plugin information.
"""
description: str = Field(..., description="Plugin description.")
name: str = Field(..., description="Plugin name.")
class PositionValue(BaseModel):
"""
Position value for serialization.
"""
units: AmountValue = Field(..., description="The units.")
class PostingCostJson(BaseModel):
"""
A posting cost in JSON-serializable form.
"""
currency: str | None = Field(None, description="Cost currency.")
date: str | None = Field(None, description="Acquisition date.")
label: str | None = Field(None, description="Lot label.")
number: CostNumberJson | None = Field(
None, description="Cost number (per-unit, total, or post-booking pair)."
)
class PostingJson(BaseModel):
"""
A posting in JSON-serializable form.
"""
account: str = Field(..., description="Account name.")
cost: PostingCostJson | None = Field(None, description="Cost specification.")
flag: str | None = Field(
None,
description='Posting-level flag (e.g., `"!"` for pending). Mirrors\n`rustledger_core::Posting::flag`.',
)
meta: dict[str, MetaValueJson] | None = Field(
None,
description="Posting-level metadata (issue #1168). Empty when the posting\nhas no explicit metadata.",
)
price: AmountValue | None = Field(None, description="Price annotation.")
units: AmountValue | None = Field(None, description="Units (amount).")
class ReferenceKind(RootModel[str]):
root: str = Field(..., description="The kind of symbol being referenced.")
class Severity(RootModel[str]):
root: str = Field(..., description="Error severity level.")
class SymbolKind(RootModel[str]):
root: str = Field(..., description="The kind of a symbol.")
class TypedValueJson(BaseModel):
"""
Tagged-union wire-format for a [`rustledger_core::MetaValue`] that
preserves the host's variant tag.
Used **only** in `DirectiveJson::Custom`'s `values` field, where
callers genuinely need to distinguish (for example) a `Date` from
a `String` or an `Account` — all three of which collapse to a bare
JSON string under the untagged [`MetaValueJson`] shape.
Wire shape: `{"type": "<variant>", "value": ...}` — mirrors
`rustledger-ffi-wasi::TypedValue` (see
`crates/rustledger-ffi-wasi/src/types/output.rs::TypedValue`) so
portable JS consumers see identical envelopes across both bindings.
**Why `value: MetaValueJson` and not `serde_json::Value`** —
`serde_json` is intentionally a host-only dev-dependency for this
crate (the runtime build avoids it to keep the wasm32 dep chain
small). [`MetaValueJson`] already covers every payload shape
FFI-WASI's `TypedValue` emits: `String` for the string-flavored
variants, `Bool` for `bool`, `Amount` for `amount`, `Null` for
`null`. The serialized JSON is bit-identical to FFI-WASI's.
`MetaValueJson` (untagged) is retained for the `meta` map of every
directive — there the lossy shape is intentional and matches what
FFI-WASI's metadata side also emits.
**Breaking change from #1199** for the WASM binding: pre-#1207
`Custom.values` emitted raw `MetaValueJson` values (lossy). Closes
#1207.
"""
type: str = Field(
...,
description='Variant tag — one of `"string"`, `"account"`, `"currency"`,\n`"tag"`, `"link"`, `"date"`, `"number"`, `"bool"`, `"amount"`,\n`"null"`. Matches FFI-WASI\'s tag strings exactly.\n\nRenamed via `#[ts(type = ...)]` so the discriminator is a\nstring-literal union on the TS side. The post-process script\nfurther narrows the full struct shape into a discriminated\nunion (per-variant `{type, value}` rows) -- see ADR-0004 for\nwhy the narrowing is hand-tuned rather than generator-driven.',
)
value: MetaValueJson = Field(
..., description="Variant payload (see [`MetaValueJson`] for the four shapes)."
)
class BeancountError(BaseModel):
"""
An error with source location.
**Renamed to `BeancountError` on the TS side** to avoid shadowing
the JS-builtin `Error` type. The Rust struct keeps the shorter
`Error` name for internal use; the rename is applied via
`#[ts(rename = ...)]` so consumers see a non-shadowing name.
"""
code: str | None = Field(
...,
description='Stable error code (e.g. `"P0001"` for a parse error, `"E3001"` for a\nvalidation error). `null` for errors without a code (generic processing\n/ query / plugin errors). Lets consumers branch on error type instead of\nmatching on message text.',
)
column: int | None = Field(
...,
description="Start column (1-based). `null` when the error has no source\nlocation. See `line` above for `range` rationale.",
ge=1,
)
end_column: int | None = Field(
...,
description="End column (1-based) of the error span. `null` when no span. See `line`.",
ge=1,
)
end_line: int | None = Field(
...,
description="End line (1-based) of the error span. `null` when no span. See `line`.",
ge=1,
)
file: str | None = Field(
...,
description="Source file the error came from (multi-file ledgers). `null` for the\nsingle-source WASM entry points (`parse`, `check`, …).",
)
hint: str | None = Field(
...,
description="Actionable hint for fixing the error, when one is available. `null`\notherwise.",
)
line: int | None = Field(
...,
description="Start line (1-based). `null` when the error has no source\nlocation (e.g. validation errors not tied to a span). Field is\nalways present on the wire (no `skip_serializing_if`); see the\nstruct-level `schemars(extend)` for the required-and-nullable\nrationale. `range(min = 1)` enforces the 1-based documented\ncontract on the JSON Schema side (schemars defaults to\n`minimum: 0` for u32).",
ge=1,
)
message: str = Field(..., description="Error message.")
phase: str | None = Field(
...,
description='Processing phase that produced the error: typically `"parse"`,\n`"validate"`, `"plugin"`, or `"lint"`. `null` when not\nattributable to a phase. The set is open (the loader phase is a free\nstring), so the TS type is a union of the known values plus `string` —\nconsumers get autocomplete on the common phases without rejecting others.',
)
severity: Severity = Field(..., description="Error severity.")
class CellValue2(BaseModel):
"""
Position with units and optional cost.
"""
cost: CostValue | None = None
units: AmountValue
class CellValue3(BaseModel):
"""
Inventory with positions.
"""
positions: list[PositionValue]
class CellValue(
RootModel[
str
| int
| bool
| CellValue1
| CellValue2
| CellValue3
| list[str]
| list[CellValue]
| dict[str, CellValue]
| None
]
):
root: (
str
| int
| bool
| CellValue1
| CellValue2
| CellValue3
| list[str]
| list[CellValue]
| dict[str, CellValue]
| None
) = Field(
...,
description="A cell value that serializes properly to JavaScript.\n\nUses untagged serialization to produce clean JSON output.",
)
class DirectiveJson1(BaseModel):
"""
Transaction directive.
"""
date: str
flag: str
links: list[str]
meta: dict[str, MetaValueJson] | None = None
narration: str | None = Field(
None,
description="Optional narration. Empty narrations are normalized to\n`None` in `convert.rs` so the field is absent on the wire\nin the empty case -- matches FFI-WASI's pattern (#1221).",
)
payee: str | None = Field(
None,
description="Optional payee. Mirrors FFI-WASI's shape: absent on the\nwire when `None` (closes #1221).",
)
postings: list[PostingJson]
tags: list[str]
type: Literal["transaction"]
class DirectiveJson2(BaseModel):
"""
Balance assertion.
"""
account: str
amount: AmountValue
date: str
meta: dict[str, MetaValueJson] | None = None
tolerance: str | None = Field(
None,
description="Explicit tolerance from the `~ 0.01` annotation, stringified.\nMirrors `rustledger_core::Balance::tolerance`.",
)
type: Literal["balance"]
class DirectiveJson3(BaseModel):
"""
Open account.
"""
account: str
booking: str | None = None
currencies: list[str]
date: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["open"]
class DirectiveJson4(BaseModel):
"""
Close account.
"""
account: str
date: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["close"]
class DirectiveJson5(BaseModel):
"""
Commodity declaration.
"""
currency: str
date: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["commodity"]
class DirectiveJson6(BaseModel):
"""
Pad directive.
"""
account: str
date: str
meta: dict[str, MetaValueJson] | None = None
source_account: str
type: Literal["pad"]
class DirectiveJson7(BaseModel):
"""
Event directive.
"""
date: str
event_type: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["event"]
value: str
class DirectiveJson8(BaseModel):
"""
Note directive.
"""
account: str
comment: str
date: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["note"]
class DirectiveJson9(BaseModel):
"""
Document directive.
"""
account: str
date: str
links: list[str] | None = Field(
None, description="Links attached to the document directive (issue #1144)."
)
meta: dict[str, MetaValueJson] | None = None
path: str
tags: list[str] | None = Field(
None, description="Tags attached to the document directive (issue #1144)."
)
type: Literal["document"]
class DirectiveJson10(BaseModel):
"""
Price directive.
"""
amount: AmountValue
currency: str
date: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["price"]
class DirectiveJson11(BaseModel):
"""
Query directive.
"""
date: str
meta: dict[str, MetaValueJson] | None = None
name: str
query_string: str
type: Literal["query"]
class DirectiveJson12(BaseModel):
"""
Custom directive.
`values` carries the positional arguments after the type
keyword. Each value is a [`TypedValueJson`] tagged union
(`{type, value}`) that preserves the host `MetaValue`
variant tag, so JS consumers can distinguish (for example)
a `Date` from a `String` from an `Account` — all of which
would otherwise collapse to bare JSON strings under the
untagged `MetaValueJson` shape.
Pre-#1168: `values` was dropped entirely from the JSON output.
Pre-#1207: present but emitted raw via `MetaValueJson` (lossy).
Post-#1207: emitted via `TypedValueJson` (this variant), mirroring
FFI-WASI's `Vec<TypedValue>`.
Both `values` and `meta` use `skip_serializing_if` to omit
the field when empty (consistent shape: a Custom directive
with no positional args and no metadata serializes as
`{type, date, custom_type}`, matching what the TS shape
declares via `values?` / `meta?`).
"""
custom_type: str
date: str
meta: dict[str, MetaValueJson] | None = None
type: Literal["custom"]
values: list[TypedValueJson] | None = Field(
None,
description="Positional values after the `custom TYPE` keyword. Each\nentry is a [`TypedValueJson`] (`{type, value}`) — the\ntagged shape preserves the host `MetaValue` variant tag so\nJS consumers can distinguish a `Date` from a `String` from\nan `Account` (closes #1207). Mirrors FFI-WASI's\n`Vec<TypedValue>` exactly.",
)
class DirectiveJson(
RootModel[
DirectiveJson1
| DirectiveJson2
| DirectiveJson3
| DirectiveJson4
| DirectiveJson5
| DirectiveJson6
| DirectiveJson7
| DirectiveJson8
| DirectiveJson9
| DirectiveJson10
| DirectiveJson11
| DirectiveJson12
]
):
root: (
DirectiveJson1
| DirectiveJson2
| DirectiveJson3
| DirectiveJson4
| DirectiveJson5
| DirectiveJson6
| DirectiveJson7
| DirectiveJson8
| DirectiveJson9
| DirectiveJson10
| DirectiveJson11
| DirectiveJson12
) = Field(
...,
description="A directive in JSON-serializable form.\n\nEach variant corresponds to a Beancount directive type, with fields\nrepresenting the directive's data in a JavaScript-friendly format.\n\nAll variants carry a `meta` field with user-defined key/value\nmetadata from the source (issue #1168). Empty metadata serializes\nas an absent field, so existing consumers continue to see the\npre-#1168 shape on directives without explicit metadata.",
)
class EditorDocumentSymbol(BaseModel):
"""
A document symbol for the outline view.
"""
children: list[EditorDocumentSymbol] | None = Field(
None, description="Children of this symbol (e.g., postings in a transaction)."
)
deprecated: bool | None = Field(
None, description="Whether this symbol is deprecated (e.g., closed account)."
)
detail: str | None = Field(None, description="More detail for this symbol.")
kind: SymbolKind = Field(..., description="The kind of this symbol.")
name: str = Field(..., description="The name of this symbol.")
range: EditorRange = Field(..., description="The range enclosing this symbol.")
class EditorHoverInfo(BaseModel):
"""
Hover information for a symbol.
"""
contents: str = Field(..., description="The hover content (Markdown formatted).")
range: EditorRange | None = Field(
None, description="The range of the hovered symbol (optional)."
)
class EditorReference(BaseModel):
"""
A reference to a symbol in the document.
"""
context: str | None = Field(
None, description="Human-readable context (e.g., directive type)."
)
is_definition: bool = Field(
..., description="Whether this is the defining occurrence."
)
kind: ReferenceKind = Field(..., description="The kind of reference.")
range: EditorRange = Field(..., description="The range of this reference.")
class EditorReferencesResult(BaseModel):
"""
Result of a find-references request.
"""
kind: ReferenceKind = Field(..., description="The kind of symbol.")
references: list[EditorReference] = Field(..., description="All references found.")
symbol: str = Field(..., description="The symbol being searched for.")
class FormatResult(BaseModel):
"""
Result of formatting.
"""
errors: list[BeancountError] = Field(..., description="Format errors.")
formatted: str | None = Field(
...,
description="Formatted source (if successful). Emitted as JSON `null` on\nfailure; no `skip_serializing_if`, so the field is always\npresent on the wire.",
)
class LedgerJson(BaseModel):
"""
A parsed Beancount ledger.
**Renamed to `LedgerJson` on the TS side** to avoid colliding with
the wasm-bindgen-exported `Ledger` class (the runtime wrapper that
owns the parsed data). `LedgerJson` is the wire shape; `Ledger` is
the class consumers instantiate via `Ledger.fromFiles(...)`. The
Rust struct keeps the shorter name for internal use; the rename
is applied via `#[ts(rename = ...)]`.
"""
directives: list[DirectiveJson] = Field(
..., description="All directives in the ledger."
)
options: LedgerOptions = Field(..., description="Ledger options.")
class PadResult(BaseModel):
"""
Result of pad expansion.
"""
directives: list[DirectiveJson] = Field(
...,
description="The original directives, verbatim. `Pad` directives are NOT\nremoved — consumers wanting a pads-removed view should\nfilter on directive type. The `padding_transactions` field\ncarries the synthesized P-flag transactions separately.",
)
errors: list[BeancountError] = Field(
...,
description="Pad processing errors (e.g. unused pads with no matching\nbalance assertion).",
)
padding_transactions: list[DirectiveJson] = Field(
...,
description="Generated padding transactions (synthesized P-flag, one per\npad-balance pair, multi-currency pads produce one per\ncurrency).",
)
class ParseResult(BaseModel):
"""
Result of parsing a Beancount file.
"""
errors: list[BeancountError] = Field(..., description="Parse errors.")
ledger: LedgerJson | None = Field(
...,
description='The parsed ledger (if successful). Emitted as JSON `null` when\nparsing failed entirely; no `skip_serializing_if`, so the field\nis always present on the wire (TS: `Ledger | null`, not\n`ledger?`). See the `#[schemars(extend(...))]` on the struct\nitself for the "required-and-nullable" wire-contract enforcement.',
)
class PluginResult(BaseModel):
"""
Result of running a plugin.
"""
directives: list[DirectiveJson] = Field(..., description="Modified directives.")
errors: list[BeancountError] = Field(..., description="Plugin errors/warnings.")
class QueryResult(BaseModel):
"""
Result of a BQL query.
"""
columns: list[str] = Field(..., description="Column names.")
errors: list[BeancountError] = Field(..., description="Query errors.")
rows: list[list[CellValue]] = Field(..., description="Result rows.")
class ValidationResult(BaseModel):
"""
Result of validation.
"""
errors: list[BeancountError] = Field(..., description="Validation errors.")
valid: bool = Field(..., description="Whether the ledger is valid.")
CellValue.model_rebuild()
EditorDocumentSymbol.model_rebuild()