Skip to content

v0.69.0

Choose a tag to compare

@Goldziher Goldziher released this 26 Aug 04:28
· 267 commits to main since this release
v0.69.0
2331650

Fixed

  • alef docs parsed docs.snippets.required_languages through a fence-tag-only parser while alef snippets gaps parsed the same key through a session-target-aware one. An entry of node, wasm or kotlin_android was therefore accepted by one command and rejected by the other, so alef all aborted with unknown language: node on a config its own sibling command had already validated. The resolver is now a single authority in snippets::types that both call sites use. The abort also short-circuited docs/snippet validation, so it was masking every finding behind it.
  • napi, wasm: a payload-bearing variant on a default-representation enum silently dropped its payload. An enum with no #[serde(tag/content/untagged)] — for example enum Label { A, B, Custom(String) } — was emitted by napi as a #[napi(string_enum)] with a bare Custom, variant and by wasm as a plain C-style Custom = 1,, discarding the field in both. Both backends now route such enums through the same discriminated-object emitter an explicitly tagged enum already used, so the payload round-trips in both directions. pyo3 was already correct, which is why Python preserved payloads while Node and WASM did not.
  • Generated DTOs for pyo3, napi, magnus, rustler and extendr now deserialize a container-level #[serde(from/into/try_from/transparent)] struct by delegating to the core type's own Deserialize and converting via Into. The derived field-by-field object Deserialize silently disagreed with the real wire shape, which is commonly a positional array. Delegation makes no positional assumption; a struct whose fields cannot round-trip (an unwrapped opaque field, a sanitized non-Cow field, a cfg-gated field) falls back to the derived impl rather than guessing. wasm and the seven FFI-derived backends were already unaffected. Serialize symmetry is deliberately unchanged.
  • The C ABI now emits a has_<field> presence companion for every optional struct field whose return type has no null representationOption<i32/u64/f32/f64/bool/…> and Option<Duration>. Previously None and a legitimate zero-valued Some both returned the same 0/0.0 sentinel with no way to tell them apart, so a field meaning "explicitly disabled" was indistinguishable from one meaning "apply the default". Pointer-shaped types already had a real null and are unchanged.
  • PHP now throws, naming both the field and the offending value, when a string-backed enum field does not match a known variant. It previously substituted the default or first variant, which a consumer's own core-side validate() then ran against and could not detect — an unknown value became a real, plausible, wrong one.
  • Swift's generated enum-from-string helper returns Result instead of panicking. An unrecognised wire string used to panic! inside __alef_<enum>_from_swift_string, unwinding across the swift-bridge FFI boundary, which is undefined behaviour. Every call site now propagates the error, forcing an otherwise-infallible wrapper's return type to Result<_, String> so the failure has somewhere to go.
  • Generated binding↔core conversions no longer silently drop Vec elements that fail to (de)serialize. A failed element keeps its slot instead of vanishing and shifting every later index — a shrinking collection is undetectable from the output shape, while a preserved slot is at least positionally honest.
  • alef snippets check no longer tells you to run alef build for a language that has no docs.snippets.sessions target configured at all. That advice was false — no build could change the result — and it was why running alef build and then alef snippets check produced byte-identical unresolved-dependency counts. The rollup now separates "No session configured" from the real build-ordering case, with distinct remediation for each.
  • Fixed-size arrays of primitives ([u8; N], [f64; N]) now resolve losslessly to Bytes/Vec<T> instead of being sanitized to a lossy String placeholder. resolve_type had no syn::Type::Array arm at all, so every fixed array fell through to a stringified Named type.
  • Sanitized public-API diagnostics are now driven by the sanitizer's recorded rewrite rather than by pattern-matching the String placeholder, so a field genuinely declared String is never conflated with one rewritten to String. Sanitized parameters also record original_type for the first time, which several backends already gated on and which had therefore been inert.
  • The Zig reference pages document []const u8/[]u8 at the wrapper boundary instead of a DTO's Rust type name, by asking the Zig emitter rather than re-deriving the shape. The same mapping error had Zig strings documented as [:0]const u8 — a sentinel-terminated slice that appears nowhere in generated output — for parameters, returns and struct fields alike.
  • A trait implemented from a foreign crate no longer contributes methods to the binding surface. An impl SomeFramework::Trait for Config { fn schema() … } — written to serve OpenAPI generation, a serializer, or any other tool — had its methods lifted into the public binding API, where they sanitized lossily and aborted generation. The trait filter was a denylist of std traits, so every other trait passed. A fully-qualified trait path rooted in a crate that is neither the one being extracted nor any crate contributing a type to the surface is now foreign. A single-segment path (an imported trait) is deliberately unchanged: resolving it depends on module visit order and would drop real methods.
  • The C ABI from_i32 reconstruction helper now carries a variant's #[cfg]. A variant behind #[cfg(feature = "…")] does not exist in a build without that feature, so an ungated match arm naming it was a hard compile error in the consumer's crate. Discriminants stay reserved, so numbering is stable across feature subsets.
  • C# no longer declares a scalar optional return as IntPtr. A function returning Option<u64> is exported by the FFI crate as a raw u64; C# declared the same symbol as IntPtr, read the integer bit pattern as a UTF-8 string pointer and passed it to FreeString — an arbitrary-address free. The pointer-vs-scalar decision now has one owner in ffi::type_map that C# asks; the private copy it replaced had already drifted on Option<Option<Duration>>. A direct Option<scalar> return still cannot distinguish None from Some(0); a presence channel for that position is not yet implemented.
  • kotlin_android documentation snippets no longer silently drop every field of a call's result. It was the one language backend without a render_snippet_body_with_functions override, so it fell to a trait default that discards the function registry; the call then anchored to an unrelated struct sharing its name and the field oracle correctly rejected everything beneath it.
  • Generated reference pages no longer corrupt a URL followed by punctuation. Proxy URL (e.g. "http://proxy:8080", …) rendered as <http://proxy:8080\",> because quotes were not excluded from the autolink match. Balanced parentheses and trailing slashes are still treated as part of the URL.
  • Cross-page reference links are no longer hardcoded to a .md suffix. New [docs].reference_link_style selects suffixed (default, unchanged) or extensionless for documentation sites that route without file extensions.
  • A fenced code block naming a language alef does not generate bindings for — astro, mdx, hcl — no longer fails documentation validation. It is reported at warning severity instead, naming the tag, so a typo like pythn stays visible rather than passing silently. A tag that claims a real binding target and still fails to resolve remains an error.
  • MCP prompts and resources constructed at runtime can now be declared in configuration. docs.mcp.declared covers surfaces built through calls like Prompt::new(…) rather than declared by attribute, which attribute extraction cannot see and therefore reported as nothing missing. Attribute-derived surfaces win on a name collision, and every dropped duplicate is reported once with a count. Consumers who declare nothing see identical output.
  • Sanitized method and function parameters now record original_type, mirroring the field path, so a diagnostic can name the original Rust type. Several backends already gated behaviour on that value and had been inert for parameters.

Added

  • A regression test proving that the functions alef generate actually consults to decide skip-vs-regenerate report a cache miss for byte-identical inputs once the compiled-in alef version changes. Prior coverage only compared two cache keys in isolation and never tied that difference to the real on-disk read path.