ev_lib 0.17.0

EV-invest shared Rust libraries, one per feature
Documentation
[package]
name = "ev_lib"
version = "0.17.0"
edition = "2024"
description = "EV-invest shared Rust libraries, one per feature"
license = "BlueOak-1.0.0"
repository = "https://github.com/EV-invest/lib"

[features]
# The kit has one token vocabulary: `tokens.css`.
default = []
modern = []

# Each library is opt-in.
architecture = ["dep:async-trait", "dep:serde", "dep:uuid", "dep:jiff"]

# Dep-light Dioxus UI kit (mirrors `@evinvest/uikit`). Renderer-agnostic RSX components;
# pulls only Dioxus + tailwind_fuse (the `cn!` engine). Unlike the kernel this
# library carries runtime deps — a UI kit can't be zero-dep; see AGENTS.md.
# `document`: the sonner toaster injects its stacking CSS through `document::Style`.
uikit = [
    "dep:dioxus",
    "dep:ev_lib_classes",
    "dep:tailwind_fuse",
    "tailwind_fuse?/variant",
    "dep:strum",
    # `asset` is what puts `asset!` in scope for `Fonts`; `document` is how it
    # and the sonner toaster inject their CSS.
    "dioxus?/asset",
    "dioxus?/document",
]

# PostHog product analytics (mirrors `@evinvest/analytics`). A small typed event
# API: native (Axum) async capture over `reqwest`; the Dioxus/wasm frontend POSTs
# the same payload via reqwest's fetch backend (no PostHog JS SDK). Carries runtime
# deps — opt-in, never in the kernel; see AGENTS.md.
analytics = [
    "dep:serde",
    "dep:serde_json",
    "dep:reqwest",
    "dep:dioxus",
    "dep:js-sys",
    "dep:web-sys",
    "dep:wasm-bindgen",
    "dep:wasm-bindgen-futures",
]

# Error monitoring / Sentry (mirrors `@evinvest/error-monitoring`). Native (Axum)
# on the `sentry` crate (+ its tower/tracing integrations); the Dioxus/wasm frontend
# builds a Sentry envelope in pure Rust and POSTs it via fetch, plus a panic hook.
error_monitoring = [
    "dep:serde",
    "dep:serde_json",
    "dep:sentry",
    "dep:reqwest",
    "dep:js-sys",
    "dep:web-sys",
    "dep:wasm-bindgen",
    "dep:wasm-bindgen-futures",
]

# OpenTelemetry logs + traces over OTLP/gRPC (native backends only; the browser
# never links an OTLP stack). Two `tracing` layers + a flush guard, plus tonic
# interceptors for W3C trace propagation across the money-flow services.
otel = [
    "dep:opentelemetry",
    "dep:opentelemetry_sdk",
    "dep:opentelemetry-otlp",
    "dep:opentelemetry-appender-tracing",
    "dep:opentelemetry-semantic-conventions",
    "dep:tracing-opentelemetry",
    "dep:tracing",
    "dep:tracing-subscriber",
    "dep:tonic",
]

# Frontend-only A/B testing (mirrors `@evinvest/experiments`). Cookie-bucketed
# weighted variant assignment for Dioxus; exposure is reported through an injected
# sink, so it never imports `analytics` (no cross-library coupling).
experiments = ["dep:dioxus", "dep:js-sys", "dep:web-sys", "dep:wasm-bindgen"]

# Five-locale i18n (mirrors `@evinvest/i18n`): locale registry, URL contract,
# Accept-Language negotiation, an ICU-subset formatter, the translation policy,
# and the `t!` macro. Wasm-safe and zero-dep *there* — the CLDR plural rules and
# number grouping for EV's five locales are hand-written rather than pulling in
# ICU4X, which would add megabytes of locale data to a Dioxus wasm bundle. The
# one dep, `inventory`, is native-only: it is how `t!` sites become a catalogue,
# and a shipped bundle extracts nothing.
i18n = ["dep:inventory"]

# Typed env settings (mirrors `@evinvest/settings`). The `settings!` macro reads
# env vars into a validated struct: aggregate error reporting, `= "literal"`
# defaults, `Option` = optional, `#[secret]` redaction. Env-only by design — no
# config files, no hot reload; sops/age decrypt at the shell/CI boundary and this
# library only reads the already-injected environment. Zero runtime deps.
settings = []

# Shared domain TypeObjects — PhoneNumber, Email, … — with validation.
# Zero runtime deps, wasm-safe (mirrors `@evinvest/types`).
types = []

# Producer SDK for landing-host microfrontends (see `mfe.rs` + the `mfe!` macro).
# wasm-only: the module is gated to `target_arch = "wasm32"`, so this is inert on
# native. A producer crate enables it; landing never compiles ev_lib at all.
# `wasm` pulls `dioxus?/web` + `web-sys`; `document` is for the bundle's `<link>`;
# `launch` is the `LaunchBuilder` behind `launch_into`, which a producer crate
# would otherwise have to enable itself for this feature to compile.
# `i18n` is not optional here: a producer declares its catalogue in the `mfe!`
# invocation, so an element remote cannot ship without having resolved its
# locale — which is the failure this pairing exists to make impossible.
mfe = [
    "dep:dioxus",
    "dep:wasm-bindgen",
    "dep:js-sys",
    "dioxus?/document",
    "dioxus?/launch",
    "i18n",
    "wasm",
]

# One opt-in switch that layers wasm/browser backends onto whatever other
# features are enabled — each feature contributes its own wasm needs here with
# the `pkg?/feature` syntax (no-op when that feature is off). Enable it
# per-target on the consumer side (`[target.'cfg(target_arch = "wasm32")']`),
# never globally, so native builds don't link browser backends.
wasm = ["uuid?/js", "dioxus?/web", "dep:web-sys"]

[dependencies]
async-trait = { workspace = true, optional = true }
dioxus = { workspace = true, features = ["macro", "html", "signals", "hooks"], optional = true }
ev_lib_classes = { workspace = true, optional = true }
jiff = { workspace = true, features = ["serde"], optional = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde_json = { workspace = true, optional = true }
strum = { workspace = true, optional = true }
tailwind_fuse = { workspace = true, optional = true }
uuid = { workspace = true, features = ["serde", "v4"], optional = true }

# Native (non-wasm) backends for the I/O libraries. Kept out of wasm builds so the
# browser target never links a native TLS stack or the `sentry` crate.
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]

# How `t!` sites become a catalogue (the `i18n` feature): the linker collects
# them, so a site that compiles is registered whether or not it ever renders.
# Native-only — a shipped wasm bundle extracts nothing, and stays zero-dep.
inventory = { version = "0.3", optional = true }

# OpenTelemetry (the `otel` feature). Native-only, pinned to a mutually-consistent
# 0.31-era set; `tracing-opentelemetry` tracks one minor ahead of the core crates.
opentelemetry = { version = "0.31", optional = true, features = ["trace", "logs"] }
opentelemetry-appender-tracing = { version = "0.31", optional = true }
opentelemetry-otlp = { version = "0.31", optional = true, default-features = false, features = ["trace", "logs", "http-proto", "reqwest-blocking-client"] }
opentelemetry-semantic-conventions = { version = "0.31", optional = true }
# No rt-tokio: the batch processors run on their own threads, so telemetry() can
# build before the tokio runtime exists (backends init tracing pre-runtime for Sentry).
opentelemetry_sdk = { version = "0.31", optional = true, features = ["trace", "logs"] }
reqwest = { version = "0.13", optional = true, default-features = false, features = ["rustls", "json"] }
sentry = { version = "0.48.2", optional = true, features = ["tower-axum-matched-path", "tracing"] }
tonic = { version = "0.13", optional = true, default-features = false }
tracing = { version = "0.1", optional = true }
tracing-opentelemetry = { version = "0.32", optional = true }
tracing-subscriber = { version = "0.3", optional = true, default-features = false, features = ["registry", "std"] }

# Browser backends for the I/O libraries. Enabled per-target on the consumer side
# (see the `wasm` feature); never linked into native builds.
#
# `Location` is not ours: dioxus-web 0.7.9's `history.rs` calls `window.location()`
# unconditionally but only declares `web-sys/Location` under its `devtools` feature,
# and `dioxus` depends on it with `default-features = false` — so a plain
# `dioxus/web` build fails to compile upstream. `dx build`/`dx serve` hide it by
# enabling `dioxus/devtools`; a bare `cargo check --target wasm32-unknown-unknown`
# does not. web-sys features unify across the graph, so declaring it here supplies
# the binding. Drop this once dioxus-web declares its own.
[target.'cfg(target_arch = "wasm32")'.dependencies]
js-sys = { version = "0.3", optional = true }
reqwest = { version = "0.13", optional = true, default-features = false, features = ["json"] }
wasm-bindgen = { version = "0.2", optional = true }
wasm-bindgen-futures = { version = "0.4", optional = true }
web-sys = { version = "0.3", optional = true, features = ["Document", "Window", "HtmlDocument", "Storage", "Navigator", "Element", "TransitionEvent", "Location"] }

[dev-dependencies]
ev_lib = { path = ".", features = ["architecture", "uikit", "analytics", "error_monitoring", "experiments", "settings", "types"] }

async-trait.workspace = true
axum = { workspace = true, features = ["ws"] }
dioxus = { workspace = true, features = ["signals", "hooks"] }
dioxus-liveview = { workspace = true, features = ["axum"] }
dioxus-ssr.workspace = true
jiff = { workspace = true, features = ["serde"] }
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
tokio = { workspace = true, features = ["macros", "rt", "net", "io-util", "time"] }
uuid = { workspace = true, features = ["serde", "v4"] }

[[example]]
name = "all_primitives"
required-features = ["uikit"]