The FFI contract
The SDK’s binding surface is one frozen UniFFI UDL contract,
map_native_core.udl, published verbatim below and as a plain-text
download. This file is the single source of
truth: the Kotlin, Swift, and React Native bindings are generated from
it, and the WebAssembly TypeScript mirror is regression-checked against
it.
The contract is addition-only. Existing members keep their signature and meaning, so your compiled bindings stay valid across upgrades; new capability is new members. The API reference documents the same surface as generated rustdoc; the UDL download carries the raw form machine tooling consumes.
The pages below show the contract verbatim, byte-equality checked against the source file on every publish; an edit to the contract propagates to the site through the build or the build fails.
Show the raw UDL contract
// FROZEN (M4, slice 1 — 2026-10-27) — the UniFFI contract of the Maps SDK;
// extended in M4 slice 2 with the fit-bounds constructor (`EdgeInsetsPixels`
// and the named `MapCamera` constructor), in M4 slice 3 with the async
// provider surface (the `MapSdk` object resolving the `geospatial-traits`
// provider contracts one-to-one through the `map-core` `ProviderRegistry`,
// the provider records and enums mirror of `geospatial-types`, and the
// provider failure variants of `SdkError`), in M4 slice 4 with the
// foreign-callback provider injection: the `GeocodingProvider` and
// `RasterTileProvider` `[Trait, WithForeign]` interfaces foreign code
// (Kotlin, Swift) implements and registers on `MapSdk` through the
// `register_*_from_foreign` methods, and in M6 with the navigation wiring:
// the `NavigationStateEventListener` `[Trait, WithForeign]` listener and the
// `MapNavigationController` object driving the `map-navigation` session, with
// the mirror records of the navigation state, the instruction payload and the
// policy records standing in for the native decision objects.
//
// M2's remaining developer-facing sub-item (2026-10-28 tile TTL/budget
// ruling) adds the tile-cache settings surface additively: the
// `TileCacheTerms` record and the `MapSdk::set_raster_tile_cache_time_to_live`
// / `raster_tile_cache_terms` pair over the installed tile cache (byte
// budget stays registration-time through the frozen registration method).
//
// Round 12 (owner ruling of 2026-10-29) adds the provider-failure diagnostic
// seam additively: the `ProviderFailureClassification` enum and the
// `ProviderFailureDiagnostic` record, plus the
// `MapSdk::latest_provider_failure_diagnostics` readback over the bounded
// per-object ring the foreign-callback provider adapters write when a
// foreign-implemented provider contract call fails.
//
// This file is the single source of truth for the native binding surface
// (Kotlin, Swift, and React Native via `uniffi-bindgen-react-native`; PRD §6,
// decision 1). The hand-reviewed wasm surface in `crate map-web-bindings`
// mirrors this contract, and a M4 slice keeps a contract-sync test asserting
// that parity (PRD §4.7).
//
// This slice freezes the camera/projection core of `map-rendering`: the
// anchored `MapCamera`, the free projection helpers, and the version probe,
// with names and semantics aligned one-to-one with the wasm surface.
//
// Records mirror the projection core's value shapes by field name; they are
// plain FFI records, converted on the Rust side to and from the
// `geospatial-types` `GeographicCoordinate`/`BoundingBox` and the
// `map-rendering` viewport/tile types through explicit converters, because
// FFI traits cannot be implemented for foreign-crate types. The error
// carries detail messages and mirrors the `geospatial-types` `SDKError`
// variants these calls can actually produce; camera/projection failures are
// `InvalidCoordinateInput` — the viewport-extent and box-corner failures of
// the core map to the same variant and keep their verbatim message under
// `detail` — while the provider surface maps the six `SDKError` variants the
// registry path can produce. Further variants enter the contract as later
// slices surface real error paths; none are invented here.
namespace map_native_core {
/// Version of the compiled SDK, equal to the crate's package version.
string get_sdk_compiled_version();
/// Maps a coordinate to normalized world coordinates in `[0, 1]`² (x
/// eastward from the antimeridian, y southward from the north pole);
/// latitude is clamped to the Mercator cutoffs.
[Throws=SdkError]
NormalizedWorldCoordinate coordinate_to_normalized_world(GeographicCoordinate coordinate);
/// The XYZ address of the tile containing the coordinate at the zoom
/// level.
[Throws=SdkError]
TileKeyRecord tile_key_for_coordinate(GeographicCoordinate coordinate, u8 zoom_level);
/// Every XYZ tile whose area intersects the spanning box, ordered
/// row-major from the north.
[Throws=SdkError]
sequence<TileKeyRecord> tiles_covering(BoundingBox bounding_box, u8 zoom_level);
/// The SDK default step-advance policy: the distance-to-end-of-step
/// condition with the Ferrostar recommended driving defaults.
StepAdvancePolicy default_step_advance_policy();
/// The SDK default deviation policy: the distance-threshold detector with
/// the SDK's default deviation threshold.
DeviationPolicy default_deviation_policy();
/// The SDK default reroute-recalculation policy (round 11): the
/// Ferrostar-recommended hysteresis — a 5 s cooldown and a 50 m movement
/// threshold.
ReroutePolicy default_reroute_policy();
};
/// A position on the WGS 84 ellipsoid in decimal degrees.
dictionary GeographicCoordinate {
/// Latitude in decimal degrees, valid within `[-90, 90]`.
double latitude;
/// Longitude in decimal degrees, valid within `[-180, 180]`.
double longitude;
};
/// Rectangular area between a south-west and a north-east corner; boxes
/// never span the antimeridian.
dictionary BoundingBox {
/// South-west corner (minimum latitude and longitude).
GeographicCoordinate south_west;
/// North-east corner (maximum latitude and longitude).
GeographicCoordinate north_east;
};
/// Position on the viewport in physical pixels (origin top-left, y down).
dictionary ScreenPoint {
/// Horizontal position in pixels.
double x;
/// Vertical position in pixels.
double y;
};
/// A world position in normalized world coordinates, both axes within
/// `[0, 1]`.
dictionary NormalizedWorldCoordinate {
/// Eastward position from the antimeridian.
double x;
/// Southward position from the north pole.
double y;
};
/// One web-mercator XYZ tile address.
dictionary TileKeyRecord {
/// Tile column, in `0..2^zoom_level`.
u32 tile_x;
/// Tile row, counted from the north, in `0..2^zoom_level`.
u32 tile_y;
/// Zoom level this tile belongs to.
u8 zoom_level;
};
/// Viewport dimensions in physical pixels.
dictionary ViewportPixels {
/// Horizontal extent in pixels.
double width_pixels;
/// Vertical extent in pixels.
double height_pixels;
};
/// Screen-space insets kept clear of map content: distances from each
/// viewport edge in pixels, so UI overlays — route cards, attribution, sheet
/// panels — never cover the content the camera guarantees visible.
dictionary EdgeInsetsPixels {
/// Pixels kept clear from the left viewport edge.
double left;
/// Pixels kept clear from the top viewport edge.
double top;
/// Pixels kept clear from the right viewport edge.
double right;
/// Pixels kept clear from the bottom viewport edge.
double bottom;
};
/// Failures thrown across the native binding surface of this slice. The
/// variants mirror the `geospatial-types` `SDKError` subset actually returned
/// by the frozen calls; the detail message is always verbatim.
[Error] interface SdkError {
/// A coordinate, viewport extent or box corner failed validation.
InvalidCoordinateInput(string detail);
/// The network transport failed; carries the provider's verbatim detail.
NetworkRequestFailed(string detail);
/// The provider answered with a payload the parser could not use.
ResponseParsingFailure(string detail);
/// The provider returned a documented application-level failure status.
ProviderRequestRejected(string detail);
/// The token-bucket rate limiter is exhausted; retry after the next refill.
RateLimitExhausted();
/// No provider implementation is registered for the requested contract.
ProviderNotConfigured(string contract_name);
/// A navigation input (route record or navigation policy) failed native
/// validation.
InvalidNavigationInput(string detail);
/// An overlay descriptor did not describe a usable overlay (wrong kind
/// payload, an out-of-range color channel, or a non-positive or
/// non-finite width or radius), or a surface input was structurally
/// unusable (a null, zero or ambiguous platform surface pointer, or a
/// non-positive viewport extent). Round 8 (surface slice).
InvalidRenderInput(string detail);
/// The live rendering could not proceed on the platform surface: the
/// platform handle or surface was unusable, the graphics context could
/// not be acquired, the surface format could not be negotiated or
/// configured, the frame texture could not be acquired even after the
/// stale-surface retry, or the frame's render state was busy in another
/// draw. Round 8 (surface slice).
RenderingSurfaceUnavailable(string detail);
};
/// Anchored map camera (geographic center, zoom, bearing, pitch) mirroring
/// the native `map-rendering` camera. The ground point under the center
/// always sits at the viewport center; zoom and pitch are clamped and the
/// bearing wraps into `[0, 360)` exactly like the native constructor.
interface MapCamera {
[Throws=SdkError]
constructor(GeographicCoordinate center, double zoom_level, double bearing_degrees, double pitch_degrees);
/// Builds the camera that frames a bounding box inside the viewport's
/// inset region: top-down and north-up by construction (pitch and bearing
/// zero), zoomed to the largest level keeping the whole box inside the
/// inset area and clamped to `maximum_zoom_level`; the center is anchored
/// on the inset region's center. Throws when the insets or the maximum
/// zoom are not finite usable values, when the insets are negative or
/// leave no inset area, or when the box spans no positive world area.
[Throws=SdkError, Name=fit_bounds]
constructor(BoundingBox bounding_box, ViewportPixels viewport, EdgeInsetsPixels edge_insets, double maximum_zoom_level);
/// Projects a coordinate to viewport pixels (origin top-left, y down).
/// Returns null when the point lies behind the camera.
[Throws=SdkError]
ScreenPoint? project_to_screen(GeographicCoordinate coordinate, ViewportPixels viewport, double field_of_view_degrees);
/// Unprojects a viewport pixel back onto the ground plane. Returns null
/// when the ray misses the ground (typically below the horizon at steep
/// pitch).
[Throws=SdkError]
GeographicCoordinate? screen_to_ground_coordinate(ScreenPoint screen_point, ViewportPixels viewport, double field_of_view_degrees);
/// Every tile at `tile_zoom_level` whose area intersects the visible
/// ground, sorted row-major from the north.
[Throws=SdkError]
sequence<TileKeyRecord> visible_tile_keys(ViewportPixels viewport, double field_of_view_degrees, u8 tile_zoom_level);
/// Nearest whole zoom level a tile fetch should use for this camera.
u8 tile_zoom_level();
/// Reads the camera's attribute state back verbatim as it was built
/// (round-9 asks). The getters make `fit_bounds`' chosen framing
/// readable: the zoom level the constructor actually fitted, its
/// anchored center, and the constructed bearing and pitch read back
/// through these members. Read-only: the camera carries no setters —
/// a new camera pose is a new constructor call. The UniFFI 0.32 UDL
/// parser supports no `[Attribute]` member syntax, so the getters are
/// plain methods.
GeographicCoordinate center();
double zoom_level();
double bearing_degrees();
double pitch_degrees();
/// Ground meters represented by one screen pixel at the camera's center
/// (round 10, the scale bar's helper): the equatorial ground meters per
/// pixel at the camera's zoom level times the cosine of the center
/// latitude, so the answer shrinks toward the poles exactly like the
/// projected ground. At any nonzero pitch this is an approximation valid
/// at the viewport's center column: the helper is flat-centered (no
/// unprojection fold of pitch into the answer) and documents that
/// honestly; the platform scale bars fit the meter magnitude through a
/// labeled ladder rather than reading a bare value.
[Throws=SdkError]
double ground_meters_per_screen_pixel(ViewportPixels viewport, double field_of_view_degrees);
};
// ---------------------------------------------------------------------------
// Foreign-callback provider surface (M4 slice 4). The `[Trait, WithForeign]`
// interfaces below are the provider contracts foreign code (Kotlin, Swift)
// implements to serve calls of the matching `MapSdk` method. The plain
// contract name is used — no `Foreign` prefix — because this namespace
// freezes nothing else with those names (the `geospatial-traits` traits of
// the same names live in a different crate and never cross this FFI), and
// the name a Kotlin implementor reads is the contract the SDK documents.
// Per-member `[Async]` only: interface-level `[Async]` is rejected by the
// uniffi 0.32 UDL parser (documented in `docs/udl/contracts-draft.udl`).
// The remaining provider contracts follow this same two-step pattern as
// their first native or foreign implementations demand.
// ---------------------------------------------------------------------------
/// Geocoding contract implemented by foreign code and registered on `MapSdk`
/// through `register_geocoding_provider_from_foreign`.
[Trait, WithForeign]
interface GeocodingProvider {
/// Resolves a free-form address text into candidate places.
[Async, Throws=SdkError]
sequence<PlaceResult> forward_geocode(string address_text);
/// Resolves a coordinate into candidate places describing that position.
[Async, Throws=SdkError]
sequence<PlaceResult> reverse_geocode(GeographicCoordinate coordinate);
};
/// Raster-tile contract implemented by foreign code and registered on
/// `MapSdk` through `register_raster_tile_provider_from_foreign`.
[Trait, WithForeign]
interface RasterTileProvider {
/// Fetches one web-mercator XYZ raster tile at the requested resolution.
[Async, Throws=SdkError]
RasterTileData fetch_raster_tile(
u32 tile_x,
u32 tile_y,
u8 zoom_level,
RasterImageFormat image_format,
RasterTileResolution tile_resolution
);
};
// ---------------------------------------------------------------------------
// Async provider surface (M4 slice 3). The enums and records below mirror the
// `geospatial-types` provider records one-to-one by name and field; `MapSdk`
// resolves every call through the `map-core` `ProviderRegistry`, so a missing
// registration throws `ProviderNotConfigured` with the contract name.
// ---------------------------------------------------------------------------
/// Structural precision a geocoder reached for one resolved location.
enum GeocodeQuality {
/// Rooftop or feature point.
"Point",
/// Street address matched to the building level.
"StreetAddress",
/// Street intersection.
"Intersection",
/// Street level without house number.
"Street",
/// Postal code area.
"PostalCode",
/// Settlement level (village, town, city).
"City",
/// County or administrative district.
"County",
/// First-level administrative division.
"State",
/// Sovereign country level.
"Country",
};
/// Mode of transport used to calculate a route.
enum TransportationMode {
/// Private car routing.
"Automobile",
/// Bicycle routing.
"Bicycle",
/// Walking routing.
"Pedestrian",
};
/// Length and speed conventions used when displaying a route.
enum UnitSystem {
/// Kilometers and kilometers per hour.
"Metric",
/// Miles and miles per hour.
"Imperial",
};
/// What a routing request optimizes for; the strategy applies to automobile
/// routing only (the current providers have no time-based cost model for the
/// other modes).
enum RouteStrategy {
/// Minimize expected travel time.
"Fastest",
/// Minimize traveled distance.
"Shortest",
};
/// Class of disruption reported in a traffic incident.
enum TrafficIncidentType {
/// Road construction.
"Construction",
/// Traffic-impacting event.
"Event",
/// Collision.
"Accident",
/// Weather-related disruption.
"Weather",
/// Lane or road closure.
"Closure",
};
/// Severity ranking of a traffic incident.
enum TrafficIncidentSeverity {
/// Minimal delay expected.
"Low",
/// Noticeable delay expected.
"Medium",
/// Significant delay expected.
"High",
};
/// Congestion classification of a road segment.
enum TrafficFlowCongestionLevel {
/// At or above free-flow speed.
"FreeFlow",
/// Moderately below free-flow speed.
"Slow",
/// Stop-and-go traffic.
"Congested",
/// Roadway effectively closed.
"Blocked",
};
/// Encoded image formats a raster tile provider may deliver.
enum RasterImageFormat {
/// PNG imagery.
"Png",
/// JPEG imagery.
"Jpeg",
};
/// Pixel resolution a raster tile is rendered at; `Standard` is the plain
/// tile form, `Retina` the `@2x` form with twice the texels per edge for the
/// same logical tile span.
enum RasterTileResolution {
/// One texel per logical tile pixel.
"Standard",
/// Twice the texels per logical tile pixel (the `@2x` request form).
"Retina",
};
/// One resolved location returned by forward or reverse geocoding.
dictionary PlaceResult {
/// Display string the provider produced for this place.
string structured_address_text;
/// Resolved position.
GeographicCoordinate coordinate;
/// Structural precision reached for this resolution.
GeocodeQuality geocode_quality;
};
/// Road features a routing request can ask the provider to avoid; all
/// disabled by default.
dictionary AvoidOptions {
/// Avoid toll roads.
boolean toll_roads;
/// Avoid limited-access highways and motorways.
boolean limited_access_highways;
/// Avoid ferry crossings.
boolean ferries;
/// Avoid unpaved roads.
boolean unpaved_roads;
};
/// Options controlling how a route is calculated.
dictionary RouteOptions {
/// Mode of transport.
TransportationMode transportation_mode;
/// Whether routing optimizes travel time or distance.
RouteStrategy route_strategy;
/// Display unit convention the provider should annotate.
UnitSystem unit_system;
/// Features to avoid.
AvoidOptions avoid_options;
/// Departure instant as seconds since the Unix epoch; null for
/// "depart now".
u64? departure_timestamp_seconds;
};
/// One maneuver within a route leg.
dictionary RouteStep {
/// Distance of this step in meters.
double distance_meters;
/// Travel time of this step in seconds.
double duration_seconds;
/// Maneuver classification as a stable snake-case text label; the
/// roundabout exit index (one-based) and the turn direction are carried
/// in the label (`roundabout-2`, `turn-left`); lane guidance is not part
/// of this surface yet.
string maneuver_type;
/// Human-readable instruction in the display language.
string instruction_display_text;
/// Instruction tuned for text-to-speech engines.
string instruction_speech_text;
/// Step geometry from the previous step end to this maneuver point.
sequence<GeographicCoordinate> shape;
};
/// Leg between two consecutive waypoints.
dictionary RouteLeg {
/// Leg distance in meters.
double distance_meters;
/// Leg travel time in seconds.
double duration_seconds;
/// Maneuver steps in traversal order.
sequence<RouteStep> steps;
};
/// A complete calculated route.
dictionary RouteData {
/// Total distance across all legs, in meters.
double distance_meters;
/// Total travel time across all legs, in seconds.
double duration_seconds;
/// Geographic bounds of the full route geometry.
BoundingBox bounding_box;
/// Legs, one per consecutive waypoint pair.
sequence<RouteLeg> legs;
};
/// One origin-destination cell of a distance matrix.
dictionary DistanceMatrixEntry {
/// Origin index into the request's origins.
u32 origin_index;
/// Destination index into the request's destinations.
u32 destination_index;
/// Distance for this cell in meters.
double distance_meters;
/// Travel time for this cell in seconds.
double duration_seconds;
};
/// Travel-time matrix for origins x destinations.
dictionary DistanceMatrix {
/// Entries in row-major origin-major order.
sequence<DistanceMatrixEntry> entries;
};
/// One reported disruption inside a queried bounding box.
dictionary TrafficIncident {
/// Provider-stable incident identifier.
string identifier;
/// Class of disruption.
TrafficIncidentType incident_type;
/// Severity ranking.
TrafficIncidentSeverity severity;
/// Provider display text for this incident.
string short_description_text;
/// Position where the incident starts.
GeographicCoordinate start_coordinate;
/// Position where the incident ends, if reported.
GeographicCoordinate? end_coordinate;
};
/// Flow condition for one road segment (or polyline of road segments) inside
/// the queried box, in kilometers per hour when the provider reports speeds
/// and as a congestion classification otherwise.
dictionary TrafficFlowData {
/// Segment geometry, ordered along the road.
sequence<GeographicCoordinate> shape_coordinates;
/// Currently observed speed in kilometers per hour, when reported.
double? current_speed_kilometers_per_hour;
/// Unconstrained (free-flow) speed in kilometers per hour, when reported.
double? free_flow_speed_kilometers_per_hour;
/// Congestion classification from the speed ratio or provider buckets.
TrafficFlowCongestionLevel congestion_level;
};
/// One fetched web-mercator XYZ raster tile.
dictionary RasterTileData {
/// Tile column coordinate at the given zoom level.
u32 tile_x;
/// Tile row coordinate at the given zoom level.
u32 tile_y;
/// Zoom level this tile belongs to.
u8 zoom_level;
/// Encoding of the byte payload.
RasterImageFormat image_format;
/// Pixel resolution the payload was rendered at.
RasterTileResolution tile_resolution;
/// Encoded image bytes.
sequence<u8> image_bytes;
};
/// Async provider entry point of the SDK: resolves every call of the
/// implemented `geospatial-traits` contracts through the `map-core`
/// `ProviderRegistry`. Construct the object empty, then the native
/// integrator registers providers through the Rust-side `register_*`
/// methods and foreign integrators through the `register_*_from_foreign`
/// methods (see the `[Trait, WithForeign]` interfaces above); every call
/// into an unregistered contract throws `SdkError::ProviderNotConfigured`
/// with the contract name.
///
/// The methods mirror the provider contracts one-to-one: geocoding (forward
/// and reverse), routing, distance matrix, place search, traffic incidents,
/// traffic flow and raster tiles. Elevation and offline routing follow with
/// their first concrete provider implementations (M6/M7).
interface MapSdk {
/// Builds the SDK with an unconfigured provider registry; the native
/// integrator registers providers before the first await call.
constructor();
/// Registers the geocoding implementation built by the foreign code on
/// the `GeocodingProvider` interface; the foreign implementation starts
/// serving `forward_geocode` and `reverse_geocode` once this returns.
void register_geocoding_provider_from_foreign(GeocodingProvider geocoding_provider);
/// Registers the raster-tile implementation built by the foreign code on
/// the `RasterTileProvider` interface; the foreign implementation starts
/// serving `fetch_raster_tile` once this returns.
void register_raster_tile_provider_from_foreign(RasterTileProvider raster_tile_provider);
/// Resolves a free-form address text into candidate places.
[Async, Throws=SdkError]
sequence<PlaceResult> forward_geocode(string address_text);
/// Resolves a coordinate into candidate places describing that position.
[Async, Throws=SdkError]
sequence<PlaceResult> reverse_geocode(GeographicCoordinate coordinate);
/// Calculates a route through the ordered waypoints with the given
/// options; the waypoints must all be canonical coordinates.
[Async, Throws=SdkError]
RouteData calculate_route(sequence<GeographicCoordinate> waypoints, RouteOptions route_options);
/// Calculates durations and distances for every origin-destination pair.
[Async, Throws=SdkError]
DistanceMatrix calculate_distance_matrix(
sequence<GeographicCoordinate> origins,
sequence<GeographicCoordinate> destinations,
TransportationMode transportation_mode
);
/// Returns fast completion candidates while the user types; the boundary
/// restricts the search when given.
[Async, Throws=SdkError]
sequence<PlaceResult> search_autocomplete(string query_text, BoundingBox? search_boundary);
/// Returns full search results for a settled query; the boundary
/// restricts the search when given.
[Async, Throws=SdkError]
sequence<PlaceResult> search_places(string query_text, BoundingBox? search_boundary);
/// Fetches incidents intersecting the bounding box.
[Async, Throws=SdkError]
sequence<TrafficIncident> fetch_traffic_incidents(BoundingBox bounding_box);
/// Fetches per-segment flow observations inside the bounding box.
[Async, Throws=SdkError]
sequence<TrafficFlowData> fetch_traffic_flow(BoundingBox bounding_box);
/// Fetches one web-mercator XYZ raster tile at the requested resolution.
[Async, Throws=SdkError]
RasterTileData fetch_raster_tile(
u32 tile_x,
u32 tile_y,
u8 zoom_level,
RasterImageFormat image_format,
RasterTileResolution tile_resolution
);
/// Rounds 8 (surface slice): registers the seven native MapQuest
/// provider implementations (geocoding, routing, distance matrix, place
/// search, traffic incidents, traffic flow, raster tiles) onto the
/// registry, constructed from the MapQuest consumer key with the SDK
/// defaults for base URL, rate limiting and retries — the foreign
/// mirror of the native integrator's `register_*` calls, so foreign
/// code installs the whole provider set with one call and an API key.
/// Re-registration replaces every slot. Elevation stays unregistered:
/// the elevation contract has no MapQuest provider, and elevation calls
/// keep throwing `ProviderNotConfigured`.
[Throws=SdkError]
void register_mapquest_providers(string mapquest_api_key);
/// Round 9 (offline-maps demo): registers the seven native MapQuest
/// provider implementations from one key exactly as
/// `register_mapquest_providers`, wrapping the raster-tile contract in
/// the disk response cache opened (or created) as a SQLite database at
/// `tile_cache_database_path` under an absolute byte budget of
/// `tile_cache_absolute_maximum_bytes`. Cached tile responses follow the
/// tiles category's time-to-live (thirty days by default — the
/// imagery-provider terms ceiling, PRD §6.7); a cache miss goes to the
/// network and back-fills the cache, so pre-warmed tiles keep rendering
/// whenever the network is unavailable. The path must be non-empty and
/// the byte budget positive; a cache open or quota failure throws with
/// its verbatim detail. Re-registration replaces the slots (including
/// the cached raster-tile slot).
[Throws=SdkError]
void register_mapquest_providers_with_tile_cache(string mapquest_api_key, string tile_cache_database_path, u64 tile_cache_absolute_maximum_bytes);
// --- Tile-cache settings surface (M2's remaining developer-facing
// sub-item; addition-only). The frozen
// `register_mapquest_providers_with_tile_cache` signature is untouched:
// the byte budget stays a registration-time fact (the SQLite store's
// quota is fixed when the store opens, so retuning it means
// re-registering through the frozen method), while the tile time-to-live
// is a property of the installed cache and is tuned live through the two
// members below. A per-integration registration-time alternative (a
// second registration method carrying the whole frozen parameter set
// plus one optional TTL field) was rejected: it duplicates the
// registration surface for one optional value, and the terms readback
// below already makes the applied value inspectable, so the override
// rides the installed cache instead. Re-registration replaces the slots
// and the tuning: a freshly registered cache starts again at the
// category default, and the integrator re-applies the override after
// registering.
//
// Validation ruling (PRD §6.7's provider-terms-ceiling intent): a
// requested time-to-live above the installed provider's declared ceiling
// is ACCEPTED, and the ceiling clamps what the cache stores — the terms
// ceiling is a compliance bound on what the cache may retain, not a
// preference, so the SDK must never retain beyond the provider's terms
// whatever the integrator asks for; refusing the call would instead
// break a live integration every time the provider's terms move or the
// integrator reads a stale ceiling. The ceiling and the effective
// (clamped) time-to-live are both in the `raster_tile_cache_terms`
// readback, so an integrator sees exactly what went into force. A zero
// time-to-live is refused: it would expire every entry on capture
// (a cache that stores nothing), which is almost certainly a unit
// mistake in the caller.
/// Tile-cache settings surface (M2): overrides the raster-tile cache's
/// time-to-live on the installed cache, in seconds. The tile cache must
/// be installed (registered through
/// `register_mapquest_providers_with_tile_cache`); otherwise this throws
/// `ProviderNotConfigured` naming `TileCache` as the unconfigured
/// backing store. A zero value throws `InvalidCoordinateInput` with a
/// precise detail (a zero time-to-live expires every entry on capture,
/// storing nothing). A value above the installed provider's declared
/// terms ceiling is accepted with the ceiling clamping what the cache
/// stores (see the ruling above); `raster_tile_cache_terms` reports the
/// effective value.
[Throws=SdkError]
void set_raster_tile_cache_time_to_live(u64 time_to_live_seconds);
/// Tile-cache settings surface (M2): reads the effective raster-tile
/// caching terms so a developer can display them (a settings page, or
/// the offline-maps demo's cache panel). Reports the time-to-live
/// currently in force on the installed cache (the category default
/// thirty days, or the last override this object accepted, clamped when
/// it exceeded the provider's terms ceiling), the installed provider's
/// declared terms ceiling (null when the provider declares no ceiling,
/// in which case the requested time-to-live applies verbatim), and the
/// cache category's default time-to-live. The tile cache must be
/// installed (registered through
/// `register_mapquest_providers_with_tile_cache`); otherwise this
/// throws `ProviderNotConfigured` naming `TileCache` as the
/// unconfigured backing store. Read-only: the readback never mutates
/// the cache.
[Throws=SdkError]
TileCacheTerms raster_tile_cache_terms();
/// Round-10 diagnostics (capability 6): subscribes the foreign listener
/// to the SDK's structured internal log events streamed fire-and-forget
/// from the crates' bounded ring buffers. Only events at
/// `minimum_severity` or above are delivered, and coordinates within
/// event messages are always rounded at the sink (never the API key).
/// The listener callbacks run on the threads that produce the events and
/// must never block on the caller; re-subscribing an already subscribed
/// listener replaces its severity gate.
void subscribe_to_diagnostic_events(SdkDiagnosticEventListener listener, SdkLogSeverity minimum_severity);
/// Round-10 diagnostics: drains up to `maximum_count` of the most recent
/// ring events at `minimum_severity` or above through the listener's
/// `on_diagnostic_event` (oldest first) and returns how many were
/// delivered, then (like Organic Maps' and OsmAnd's opt-in capture)
/// nothing is removed from the ring, so a later flush can redeliver. The
/// flush is bounded and synchronous: a listener must not block on it.
u32 flush_recent_diagnostic_events(SdkDiagnosticEventListener listener, SdkLogSeverity minimum_severity, u32 maximum_count);
/// Round-12 provider-failure diagnostic seam (addition-only): reads the
/// retained diagnostics of every failed foreign-callback provider call
/// this object has served — the failures foreign implementations of the
/// `[Trait, WithForeign]` provider contracts threw, both the typed ones
/// (the foreign code threw one of the contract's `SdkError` variants) and
/// the unclassified ones (the foreign code failed without a mapped
/// error). Returns the ring's retained entries oldest first, in
/// recency-bounded order: the ring keeps the last
/// `PROVIDER_FAILURE_DIAGNOSTIC_RING_CAPACITY` failures and drops the
/// oldest beyond it, and reading is non-destructive. Read-only: the
/// readback never records anything and never clears the ring. See
/// `ProviderFailureDiagnostic` for what an entry carries; the frozen
/// `SdkError` variants are untouched — failures still surface typed at
/// the throwing call, and this seam only adds where they can be
/// classified after the fact.
sequence<ProviderFailureDiagnostic> latest_provider_failure_diagnostics();
};
// ---------------------------------------------------------------------------
// Tile-cache settings surface (M2 — addition-only). The record mirrors the
// effective caching terms of the installed raster-tile cache; the ceiling is
// the provider-specific caching term (PRD §6.7), read only when a cache is
// installed, which is the only state the settings surface addresses.
// ---------------------------------------------------------------------------
/// The effective raster-tile caching terms of the installed cache
/// (`MapSdk::raster_tile_cache_terms`'s return), shaped so a developer can
/// display the caching posture: the default, the provider's ceiling and the
/// value currently in force.
dictionary TileCacheTerms {
/// Time-to-live currently in force on the installed cache, in seconds:
/// the category default, or the last accepted override (clamped to the
/// ceiling when the override exceeded it).
u64 effective_time_to_live_seconds;
/// The installed provider's declared caching-terms ceiling in seconds,
/// or null when the provider declares no ceiling — the requested
/// time-to-live then applies verbatim. For the MapQuest raster tiles
/// this is the documented terms ceiling (thirty calendar days).
u64? provider_terms_ceiling_seconds;
/// The cache category's default time-to-live for raster tiles, in
/// seconds (thirty days by the owner ruling of 2026-10-28, PRD §6.7);
/// the value a freshly registered cache starts at.
u64 default_time_to_live_seconds;
};
// ---------------------------------------------------------------------------
// Navigation wiring (M6). The `[Trait, WithForeign]`
// `NavigationStateEventListener` is the listener contract foreign code
// implements; its subscriptions register on the `MapNavigationController`
// object, which drives the native `map-navigation` reducer session and its
// per-topic broadcaster. The records and enums below mirror the
// `map-navigation` plain-data types field-for-field; the step-advance and
// deviation policies stand in for the native decision objects (trait
// objects), which cannot cross the FFI, as tagged records. Payload-carrying
// native enums mirror as a kind enum plus nullable fields; the instruction
// payload mirrors without the native active maneuver's lane-guidance list,
// which stays native until the frozen route records carry lane guidance.
// ---------------------------------------------------------------------------
/// State-listener contract implemented by foreign code and subscribed on
/// `MapNavigationController` through the `subscribe_to_*` methods; each
/// registered subscription delivers only its own topic.
[Trait, WithForeign]
interface NavigationStateEventListener {
/// Receives the composed instruction payload of every dispatched state
/// change (the instructions topic).
void on_navigation_instruction(NavigationInstruction instruction);
/// Receives a deviation-status stream update (the deviation topic):
/// off-route on entry and on every recorded measurement update while the
/// deviation persists, on-route when it clears.
void on_deviation_status_change(DeviationStatusChange deviation_status_change);
/// Receives one phase transition per dispatched trip-state transition
/// (the phase topic).
void on_navigation_phase_change(NavigationPhaseChange navigation_phase_change);
/// Round 10 (speech pacing, OQ5's SDK-owned triggers): receives one
/// utterance trigger when the controller's distance-band policy judges
/// the fix newsworthy (a trigger band crossed this fix) — the verbatim
/// composed speech text, never rewritten, so the platform owns only
/// playback. Subscribers of the speech topic receive exactly the
/// `subscribe_to_speech_instructions` deliveries.
void on_speech_instruction(SpeechInstruction speech_instruction);
};
/// Handle coupled to exactly one `(topic, listener)` subscription; cancelling
/// is explicit and idempotent, dropping the handle does not unsubscribe.
interface NavigationTopicSubscription {
/// Cancels this subscription: the listener stops receiving the
/// subscription's topic from the next dispatch on. Idempotent.
void cancel();
};
/// The exported navigation controller: drives the native navigation reducer
/// session, broadcasts every state change to the registered listeners, and
/// answers read-only state and instruction queries. Obtain routes from
/// `MapSdk::calculate_route` and start a trip on them with this controller.
///
/// The listener callbacks run synchronously on the thread that drives the
/// controller call; a listener calling back into the controller while a
/// dispatch is still in flight is suppressed by the broadcaster's
/// single-in-flight guard (the state change behind it is already applied,
/// only its broadcast is skipped). The controller never holds its state lock
/// across a listener callback.
interface MapNavigationController {
/// Builds the controller with no active trip and no subscriptions.
constructor();
/// Subscribes the listener to the instructions topic: the composed
/// instruction payload of every dispatched state change, in registration
/// order.
NavigationTopicSubscription subscribe_to_navigation_instructions(NavigationStateEventListener listener);
/// Subscribes the listener to the deviation-status topic, in registration
/// order.
NavigationTopicSubscription subscribe_to_deviation_status(NavigationStateEventListener listener);
/// Subscribes the listener to the phase-transition topic, in registration
/// order.
NavigationTopicSubscription subscribe_to_phase_changes(NavigationStateEventListener listener);
/// Begins navigating `route` with the given policies, replacing any
/// active trip. The initial instruction payload is dispatched; no
/// deviation or phase transitions exist yet. A route without traversable
/// steps represents an already-arrived trip. Refuses policies with
/// non-finite or negative thresholds, and route records carrying a step
/// maneuver label the conversion cannot map.
[Throws=SdkError]
void start_trip(RouteData route, UnitSystem unit_system, StepAdvancePolicy step_advance_policy, DeviationPolicy deviation_policy);
/// Delivers one position fix to the active trip; without one the fix is
/// dropped. Fixes without an accuracy report never trigger a step
/// advance and are never judged by the deviation detector.
void provide_location_update(NavigationLocationUpdate location_update);
/// Replaces the route of the active trip (a reroute). The replacement
/// route must be anchored at the trip's current snapped coordinate; the
/// step advance strategy for the new route is configured by
/// `step_advance_policy` and the deviation policy persists. A reroute
/// offered to an arrived trip is ignored (no transition); without an
/// active trip the event is dropped. Round 11: an in-flight reroute
/// calculation is settled by this call (applied or ignored alike), so
/// the request-in-flight gate reopens after the integrator acts.
[Throws=SdkError]
void provide_reroute(RouteData route, StepAdvancePolicy step_advance_policy);
/// Round 11 (Ferrostar B3): applies the reroute-recalculation gates to
/// one reroute-request signal at the wall clock: true when every gate
/// passed (no calculation in flight, outside the cooldown window, and
/// — when both fix references exist — beyond the movement threshold)
/// and a calculation is now in flight, readable as the trip state's
/// `is_calculating_new_route`; false when the signal was refused. A
/// signal without an open, still-navigable trip (no trip, or the trip
/// has arrived) is refused before the gates are consulted. A passing
/// caller owes a settle: the eventual `provide_reroute` (applied or
/// ignored) or `cancel_reroute_calculation`. The initial (trip's first)
/// attempt always passes the cooldown and movement gates.
boolean begin_reroute_calculation();
/// Round 11: settles an in-flight reroute calculation without applying a
/// route (the integrator abandoned the request): the request-in-flight
/// gate reopens. The cooldown keeps running from the calculation's
/// beginning and the movement reference stays where it was. Idempotent.
void cancel_reroute_calculation();
/// Round 11: swaps the reroute-recalculation hysteresis policy in force,
/// keeping the gate's memory as it stands. Both thresholds must be
/// finite and non-negative (refused with `InvalidNavigationInput`
/// otherwise, and the policy in force is then untouched).
[Throws=SdkError]
void configure_reroute_policy(ReroutePolicy reroute_policy);
/// The plain state of the active trip, or null without one.
TripNavigationState? current_trip_state();
/// The composed turn-by-turn instruction payload of the current trip
/// state, or null without an active trip; repeated reads of an unchanged
/// state return the same payload.
NavigationInstruction? current_instructions();
/// Round 10 (speech pacing, OQ5): subscribes the listener to the
/// speech-instruction trigger topic, in registration order. Each fix's
/// dispatch consults the controller's named-potential policy: the trigger
/// bands reuse the display phrase's distance bands (so a spoken "in 100
/// meters" always matches the payload's shown phrase band), each band
/// announces at most once per step (monotone progress), and a minimum
/// inter-utterance spacing and repeat distance suppress chatter. The
/// platform keeps playback (including its fallback speak-latest behavior
/// when no listener subscribes) and synthesis stays out of scope.
NavigationTopicSubscription subscribe_to_speech_instructions(NavigationStateEventListener listener);
};
/// High-level phase of a trip that has already been started. Rerouting is not
/// a phase of its own: the machine flows Navigating ⇄ OffRoute within a trip
/// and Navigating → Arrived at its end.
enum NavigationPhase {
/// Following the route's step sequence.
"Navigating",
/// The deviation detector judged the last graded fix off route; progress
/// is frozen at the last on-route values until a graded fix recovers or a
/// reroute replaces the route.
"OffRoute",
/// The final step has been completed; location updates no longer change
/// the state.
"Arrived",
};
/// Which grade of deviation a recorded deviation carries: the two-grade
/// distinction between off the current step yet still on the route line and
/// completely off the route. Only the fully-off grade freezes navigation; the
/// wrong-step grade never freezes on its own.
enum DeviationGradeKind {
/// The fix projects onto neither the current step nor any future step of
/// the remaining route line: the freezing grade. The trip's progress is
/// frozen until a graded fix recovers or a reroute replaces the route.
"FullyOffRoute",
/// The fix is off the current step but projects onto a future step of the
/// remaining route line: "on route, wrong step". The trip keeps
/// navigating and counting down, and is never frozen by this grade alone.
"OffStepOnRoute",
};
/// Plain-data description of an active deviation from the route. The grade
/// kind distinguishes the freezing fully-off grade from a merely wrong-step
/// record: a wrong-step record is guidance feedback ("on the route, on a
/// later step") carried while the trip phase stays `Navigating`.
dictionary DeviationInfo {
/// Which grade of deviation this is (see `DeviationGradeKind`).
DeviationGradeKind grade_kind;
/// Distance, in meters, from the raw fix that triggered (or most recently
/// confirmed) the deviation to the remaining route line.
double deviation_from_route_meters;
};
/// Location of one route step in traversal order across a route's legs.
dictionary RouteStepPosition {
/// Index of the leg that owns this step in the route's legs.
u64 leg_index;
/// Index of the step within its leg in the leg's steps.
u64 step_index;
};
/// Progress along the remaining trip.
dictionary TripProgress {
/// Distance, in meters, from the snapped position to the end of the
/// current step — the point of the next maneuver.
double distance_to_next_maneuver_meters;
/// Total remaining distance, in meters, including the current step's
/// remainder.
double distance_remaining_meters;
/// Total remaining travel time, in seconds, measured the same way as
/// `distance_remaining_meters`.
double duration_remaining_seconds;
};
/// The full navigable state of an active trip, read-only for foreign code.
/// The route's steps are carried as traversal-ordered indices, never
/// duplicated; the live step-advance condition and deviation detector are not
/// records and cannot cross the FFI — they are configured through the
/// navigation policies on the controller.
dictionary TripNavigationState {
/// Current phase of the trip.
NavigationPhase phase;
/// The route being navigated.
RouteData route;
/// All steps across every leg, flattened in traversal order.
sequence<RouteStepPosition> step_positions;
/// Index into `step_positions` of the step currently traversed.
u64 current_step_position;
/// Progress along the remaining trip.
TripProgress progress;
/// Position snapped onto the current step's shape; null until the first
/// location update has been processed.
GeographicCoordinate? snapped_coordinate;
/// Active deviation, as judged by the deviation detector; a fully-off
/// grade is set exactly while the phase is `OffRoute`, and a wrong-step
/// (`OffStepOnRoute`) grade may be set while the phase stays `Navigating`.
DeviationInfo? deviation;
/// Round 11 (Ferrostar B3): whether a reroute recalculation is currently
/// in flight. This is the controller's wall-clock gate fact, not reducer
/// state — the reducer stays clock-free — so it is read fresh from the
/// controller on every readback.
boolean is_calculating_new_route;
};
/// Length unit a distance-phrase magnitude is expressed in.
enum DistanceUnit {
/// Meters, used by the metric near and moderate bands.
"Meters",
/// Kilometers, used by the metric far band.
"Kilometers",
/// Feet, used by the imperial near and moderate bands.
"Feet",
/// Miles, used by the imperial far band.
"Miles",
};
/// Which form a distance phrase takes.
enum DistancePhraseKind {
/// The position is closer than half a near-band increment to the maneuver
/// point; no distance is phrased and composed text drops the prefix. The
/// magnitude is null on this kind.
"Immediate",
/// A rounded distance magnitude in a specific unit; the magnitude and
/// unit are set on this kind.
"Quantity",
/// No distance is phrased because the freezing (fully-off-route) deviation
/// grade holds: the projection is frozen, so any countdown composed off it
/// would be a phantom measurement. The magnitude is null on this kind, and
/// the composed text channels carry no distance prefix.
"Suppressed",
};
/// The unit-system-aware phrasing of the distance remaining to a maneuver,
/// rounded into the named distance phrasing bands.
dictionary DistancePhrase {
/// Which form the phrase takes.
DistancePhraseKind phrase_kind;
/// Rounded magnitude of the phrase; null for the immediate and suppressed
/// forms.
double? magnitude;
/// Unit the magnitude is expressed in; null for the immediate and
/// suppressed forms.
DistanceUnit? magnitude_unit;
};
/// Which form an instruction text takes.
enum ManeuverInstructionTextKind {
/// The provider authored instruction text for this maneuver.
"Composed",
/// The provider authored no instruction text for this maneuver; nothing
/// is invented to fill it. All four text fields are null on this kind.
"Unavailable",
};
/// The composed text channels of one maneuver: provider-authored text is
/// carried verbatim beside the composed strings.
dictionary ManeuverInstructionText {
/// Which form the text takes.
ManeuverInstructionTextKind text_kind;
/// Provider-authored display text, verbatim and never rephrased.
string? provider_display_text;
/// Provider-authored speech text, verbatim and never rephrased.
string? provider_speech_text;
/// Banner variant: the live distance prefix plus the display variant.
string? composed_display_text;
/// Text-to-speech variant: the live distance prefix plus the speech
/// variant.
string? composed_speech_text;
};
/// Instruction payload of the maneuver currently being traversed (the banner
/// maneuver: the step the snapped position sits inside, with the distance
/// measured to that step's end — the maneuver point).
dictionary ActiveManeuverInstruction {
/// Composed display and speech text of the active step.
ManeuverInstructionText instruction;
/// Unit-system-aware distance phrasing of the distance to the next
/// maneuver.
DistancePhrase distance_phrase;
/// Structured exit index of a roundabout active step, starting at one.
u8? roundabout_exit_index;
};
/// Instruction payload of the maneuver after the active one (the "then"
/// instruction), text-carrying only.
dictionary UpcomingManeuverInstruction {
/// Composed display and speech text of the following step.
ManeuverInstructionText instruction;
/// Structured exit index when the following step is a roundabout.
u8? roundabout_exit_index;
};
/// The emitted turn-by-turn instruction payload for one trip state; the same
/// state yields the same payload on every read.
dictionary NavigationInstruction {
/// The banner maneuver currently being traversed.
ActiveManeuverInstruction active_maneuver;
/// The maneuver after the active one, where the route has a following
/// step (always null on the route's final step and after arrival).
UpcomingManeuverInstruction? upcoming_maneuver;
/// True exactly while the trip has arrived and the remaining progress is
/// zero.
boolean is_arrival;
/// True exactly while the trip is off route: the payload beneath the
/// marker is the frozen last-on-route projection, whose distance phrase
/// is suppressed rather than presented, and must not be presented as
/// current guidance.
boolean is_rerouting_required;
};
/// Which form a deviation-status update takes.
enum DeviationStatusChangeKind {
/// The trip is judged deviating; the deviation description is set on this
/// kind. The description's grade kind distinguishes the freezing
/// fully-off grade from a merely wrong-step record, which is delivered
/// while the trip phase stays `Navigating`.
"OffRoute",
/// A previously recorded deviation cleared; the trip is judged on route.
"OnRoute",
};
/// A deviation-topic update: the detector's judgment changed between the
/// previous and the current trip state — entry, measurement drift while the
/// deviation persists, and clearing.
dictionary DeviationStatusChange {
/// Which form the update takes.
DeviationStatusChangeKind status_change_kind;
/// The recorded measurement of the deviation, set exactly on the
/// off-route kind.
DeviationInfo? deviation_info;
};
/// A phase-transition event delivered on the phase topic.
enum NavigationPhaseChange {
/// The trip entered the off-route phase from the navigating phase.
"OffRouteEntered",
/// A graded fix back on the route line lifted the deviation and returned
/// the trip to the navigating phase.
"OffRouteCleared",
/// A reroute event replaced the route (delivered only when the event
/// actually applied — an ignored reroute produces no transition).
"RerouteApplied",
/// The final step completed; the trip reached the arrived phase.
"Arrived",
};
/// One raw position fix delivered to the navigation controller.
dictionary NavigationLocationUpdate {
/// Raw position on the WGS 84 ellipsoid, before route snapping.
GeographicCoordinate coordinate;
/// Radius, in meters, of the 68% horizontal-accuracy envelope of this fix
/// (null when the source does not report accuracy).
double? horizontal_accuracy_meters;
/// Direction of travel in degrees clockwise from true north (null when
/// the source does not report a course).
double? course_heading_degrees;
/// Fix instant in seconds since the Unix epoch (null for untimed
/// sources).
double? timestamp_seconds;
};
/// Which step-advance strategy a navigation policy configures.
enum StepAdvancePolicyKind {
/// Steps change only when driven programmatically (not available on this
/// surface yet); the trip still tracks progress. The threshold fields are
/// ignored on this kind.
"Manual",
/// Advances when the position fix comes within the threshold of the end
/// of the current step, eager-advance semantics.
"DistanceToEndOfStep",
/// Advances when the position fix moves farther from the current step's
/// polyline than the threshold, delayed-advance semantics.
"DistanceFromStep",
};
/// The step-advance policy of a trip, standing in for the native decision
/// object that cannot cross the FFI.
dictionary StepAdvancePolicy {
/// Which step-advance strategy the policy configures.
StepAdvancePolicyKind policy_kind;
/// Distance, in meters, of the advance threshold (the distance to the
/// step's end, or from the step's polyline, depending on the kind).
double advance_threshold_meters;
/// Fixes with a reported horizontal accuracy worse than this, in meters,
/// never advance.
double minimum_horizontal_accuracy_meters;
};
/// Which deviation-detection strategy a deviation policy configures.
enum DeviationPolicyKind {
/// Flags a deviation when the raw fix is farther from the remaining route
/// line than the configured threshold, widened by the fix's reported
/// accuracy.
"DistanceThreshold",
};
/// The deviation-detection policy of a trip, standing in for the native
/// decision object that cannot cross the FFI.
dictionary DeviationPolicy {
/// Which deviation-detection strategy the policy configures.
DeviationPolicyKind policy_kind;
/// Distance, in meters, from the remaining route line beyond which a fix
/// is judged off route (before any accuracy widening, strictly greater).
double deviation_threshold_meters;
};
// ---------------------------------------------------------------------------
// Round 11 (Ferrostar adoption batch 2, mechanism adopted from `FerrostarCore`
// under its BSD 3-Clause license; the expressions are our own). The
// reroute-recalculation hysteresis: three gates stand between a fully
// off-route signal and an actual recalculation request — the request-in-flight
// gate, the cooldown and the movement threshold — plus the in-flight
// readback on the trip state, so a jittery off-route bounce cannot spam the
// integrator's routing engine. The gates live at the controller seam with
// the wall clock (exactly like the speech pacing's planner), never in the
// reducer: the reducer stays a pure, clock-free event function. The gates
// close the round-9 contract gap ("no reroute timestamp on the phase
// channel") without an addition there: a reroute timestamp on the phase
// channel is not addition-only (`NavigationPhaseChange` stays a payloadless
// flat enum), so the phase channel stays untouched and the honest closure is
// the gate seam plus the `is_calculating_new_route` readback plus
// `begin_reroute_calculation`'s boolean answer.
// ---------------------------------------------------------------------------
/// The reroute-recalculation hysteresis policy of a navigation controller
/// (round 11, Ferrostar B3's mechanism). Both thresholds must be finite and
/// non-negative; the controller refuses otherwise (`InvalidNavigationInput`),
/// leaving the policy in force untouched.
dictionary ReroutePolicy {
/// Minimum wall-clock seconds between the beginnings of two reroute
/// calculations (the SDK default: 5).
double recalculation_cooldown_seconds;
/// Minimum displacement, in meters, between the raw position fix current
/// at a calculation's beginning and the one current at the previous
/// calculation's beginning (the SDK default: 50).
double recalculation_movement_meters;
};
// ---------------------------------------------------------------------------
// Native surface views (round 8, slice 0). The additions below mirror the
// wasm canvas' overlay plain-object names — `routePolyline`,
// `filledPolygon`, `pointMarkers`, `trafficFlow` — as a kind enum plus
// nullable fields (the round-7 navigation payload pattern: a field-carrying
// native enum lower as a tag with `null` marking the unused payload
// fields), and expose one `MapSurfaceView` object: the FFI face of the
// `map-rendering` surface renderer, drawn by a platform view the foreign
// code owns (Kotlin `SurfaceView`, Swift `UIView`/`MKMapView`-style view,
// PRD §4.4–4.6). The renderer itself stays native (same `wgpu` machinery as
// the headless capture path); the FFI surface carries the lifecycle
// (attach/resize/detach), the overlay list, and an explicit per-frame draw.
// The platform's own render cadence drives the view — `draw` is per-frame,
// called from a display link or choreographer callback, never an SDK-
// internal loop: no SDK thread this slice, documented below.
// ---------------------------------------------------------------------------
/// Which overlay shape one descriptor configures. The kind tags follow the
/// wasm canvas' plain-object `kind` strings (the wasm seam passes the
/// lowercase camel kinds `routePolyline`/`filledPolygon`/`pointMarkers`/
/// `trafficFlow`; the FFI enum variants are the contract's own CamelCase),
/// mirroring the native `MapOverlay` payloads one to one.
enum MapOverlayKind {
/// A routed path drawn as a thick line.
"RoutePolyline",
/// A filled polygon: one outer ring, ear-clip triangulated.
"FilledPolygon",
/// Screen-facing point markers (octagon fans).
"PointMarkers",
/// Congestion-colored traffic-flow lines.
"TrafficFlow",
};
/// Solid RGBA overlay style, every channel in `[0, 1]`. Mirrors the wasm
/// canvas' `color` plain object. Null style fields keep the documented
/// `map-rendering` default style of the matching overlay kind.
dictionary OverlayColor {
/// Red channel in `[0, 1]`.
double red;
/// Green channel in `[0, 1]`.
double green;
/// Blue channel in `[0, 1]`.
double blue;
/// Alpha channel in `[0, 1]` (the overlay pipeline alpha-blends).
double alpha;
};
/// One overlay shape drawn over the map surface's tiles in every frame,
/// selected by `kind`. Each kind reads exactly its own payload fields and
/// ignores the others; a descriptor whose kind-required payload is absent
/// (or whose style field is unusable) rejects the whole `set_overlays` call
/// and keeps the previous list in force — the wasm canvas' all-or-nothing
/// semantics. Absent style fields keep the documented map-rendering
/// default style; present style fields must be usable (channels in
/// `[0, 1]`, widths and radii positive and finite). The record mirrors the
/// native `map-rendering` overlay structs field-for-field; the wasm canvas
/// plain objects carry the same names in camel case (`ringCoordinates`,
/// `pointMarkerPositions`, `pointMarkerRadiusPixels`,
/// `trafficFlowSegments`).
dictionary MapOverlayDescriptor {
/// Which overlay shape this descriptor configures and which payload
/// fields carry it.
MapOverlayKind kind;
/// `RoutePolyline`: the traversal-order polyline; consecutive equal
/// coordinates skip their degenerate segment. Required for the kind.
sequence<GeographicCoordinate>? coordinates;
/// Route polyline, filled polygon and point markers: solid color
/// override, or null for the kind's documented default.
OverlayColor? rgba_color;
/// Route polyline and traffic flow: line width in screen pixels
/// (positive and finite), or null for the kind's documented default.
double? width_pixels;
/// `FilledPolygon`: the outer ring in traversal order without a
/// repeated closing vertex (a repeated one is tolerated and dropped).
/// Required for the kind.
sequence<GeographicCoordinate>? ring_coordinates;
/// `PointMarkers`: the geographic anchor of every marker. Required for
/// the kind.
sequence<GeographicCoordinate>? point_marker_positions;
/// Point markers: marker radius in screen pixels (positive and finite),
/// or null for the documented default.
double? point_marker_radius_pixels;
/// `TrafficFlow`: the flow observations to paint, one per road segment
/// (or polyline of segments), colored by `congestion_level` — the same
/// `TrafficFlowData` records the `fetch_traffic_flow` call returns.
/// Required for the kind.
sequence<TrafficFlowData>? traffic_flow_segments;
};
/// The platform window surface handle foreign code hands to
/// `MapSurfaceView::attach`: exactly one platform pointer is set, the rest
/// stay null (a null-or-ambiguous handle is refused with
/// `SdkError::InvalidRenderInput`; a null pointer is `u64 0` or null, never
/// a zeroed pointer passed through). The handle shape is a single record
/// with one nullable pointer per supported platform — one dictionary keeps
/// add-a-platform growth additive and mirrors how uniffi records lower
/// everywhere. The object behind the pointer must outlive the attached
/// view: the raw handles are the caller's validity guarantee (see
/// `wgpu::SurfaceTargetUnsafe`), and detach/drop releases the Rust side
/// first, so dropping the platform view before `detach` is the caller's
/// bug.
dictionary MapSurfaceTargetHandle {
/// macOS: pointer to the `NSView` the surface renders into (Metal
/// creates the layer from the view); null on the other platforms.
u64? appkit_view_pointer;
/// iOS: pointer to the `UIView` the surface renders into; null on the
/// other platforms.
u64? ios_view_pointer;
/// Android: pointer to the `ANativeWindow` obtained from the platform
/// `Surface` (`ANativeWindow_fromSurface`); null on the other platforms.
u64? android_native_window_pointer;
};
/// The native map surface view: renders the camera-driven tiled map plus
/// the overlay list onto a live platform window surface through the same
/// `wgpu` machinery as the headless capture path. Construct with the
/// `MapSdk` whose registered raster-tile provider the attached surface
/// draws from (the provider resolves through the registry at attach time —
/// with no registered provider, `attach` throws
/// `SdkError::ProviderNotConfigured`).
///
/// # Lifecycle
///
/// `attach` wraps the platform handle, acquires the surface-compatible
/// graphics context, negotiates the surface format, configures the initial
/// extent and resolves the tile provider — every step's failure throws
/// (`SdkError::RenderingSurfaceUnavailable` for the GPU/surface path,
/// `InvalidRenderInput` for a bad handle or extent, `ProviderNotConfigured`
/// for the missing provider slot). `resize` (explicit, called from the
/// platform's surface-size callback) reconfigures the surface; a zero
/// extent rejects. `detach` releases the surface state until a re-attach.
/// Overlay state outlives attach/detach cycles: `set_overlays` and
/// `clear_overlays` work detached, and the next attach draws the stored
/// list.
///
/// # Draw model
///
/// Frames are explicit: the foreign platform view calls `draw` once per
/// rendered frame from its own cadence (a display link, a choreographer, a
/// resize-then-once pattern) with the current `MapCamera` value — there is
/// deliberately no SDK-internal render thread or continuous redraw loop in
/// this slice. Each draw lists the camera's visible tiles, fetches the
/// pooled misses from the surface-attached provider (budgeted per frame,
/// parent fallback like the headless path), and renders tiles then
/// overlays alpha-blended, presenting to the surface. A failed frame
/// throws and leaves the previous frame on screen. Draws on one view are
/// serialized: a draw holding the view's render state (the fetch phase
/// spans network await) reports a concurrent `draw` or `resize` with
/// `RenderingSurfaceUnavailable`, the foreign cadence retrying the next
/// frame.
interface MapSurfaceView {
/// Builds the view over the SDK state it draws from.
constructor(MapSdk map_sdk);
/// Attaches the platform surface of the given initial physical-pixel
/// extent (the extent must be non-zero; a platform view not laid out
/// yet waits for `resize`). The wrap validates the handle, acquires
/// the context, negotiates the format, configures and resolves the
/// raster provider. Attaching an attached view is refused.
[Throws=SdkError]
void attach(MapSurfaceTargetHandle surface_handle, u32 width_pixels, u32 height_pixels);
/// Reconfigures the surface to the new physical-pixel extent (both
/// non-zero); a mid-draw resize surfaces the busy state and the next
/// frame's draw or the next resize call picks it up.
[Throws=SdkError]
void resize(u32 width_pixels, u32 height_pixels);
/// Detaches: releases the surface, its renderer and its scheduler.
/// Overlay state persists; re-attach rebuilds the drawing machinery.
/// Safe against a draw still in flight (its frame completes and the
/// state is then dropped without being restored).
void detach();
/// Replaces the overlay list drawn over the tiles in every following
/// `draw`, replacing any earlier list in order (later entries draw
/// over earlier ones); an empty list clears. Every descriptor must be
/// valid for its kind: no partial lists are kept.
[Throws=SdkError]
void set_overlays(sequence<MapOverlayDescriptor> overlay_descriptors);
/// Clears the overlay list; following frames draw the tiles alone
/// until `set_overlays` supplies a new list.
void clear_overlays();
/// Draws one frame for the camera: the camera's visible tiles fetched
/// and rendered, overlaid, presented. Returns the frame's tile and
/// overlay statistics (round-9 ask) so the platform render loop can
/// report exactly what a frame drew: drawn pixels this frame come from
/// the present and fallback tiles, while the deferred and unavailable
/// counts are where later frames may still change pixels. Throws when
/// the view is detached, a draw is already in flight, or the frame's
/// rendering failed; a thrown draw leaves the previous frame on screen.
[Async, Throws=SdkError]
TileFrameStatistics draw(MapCamera camera);
/// Round-10 (renderer-owned location indicator): sets the single
/// location layer drawn after the tile pass and the overlay pass
/// (topmost SDK layer), replacing any earlier state — mirroring
/// `set_overlays`' all-or-nothing semantics, so a state or style that
/// fails validation throws and keeps the earlier indicator. The state
/// outlives attach/detach cycles like the overlay list; the styled
/// visuals resolve to the documented map-rendering defaults for every
/// null style field.
[Throws=SdkError]
void set_location_indicator(LocationIndicatorState state, LocationIndicatorStyle style);
/// Clears the location layer; following frames draw tiles and overlays
/// alone until `set_location_indicator` supplies new state.
void clear_location_indicator();
/// Round-10 (idle-settled redraw discipline): reads whether the view's
/// presented state would not change if `draw` ran again right now —
/// true when this camera value equals the last drawn camera, the overlay
/// list and the location indicator are unchanged since that draw, and
/// the last frame's tile statistics resolved no pending pixels (no
/// fallback, deferred or unavailable tiles). A pure query: it never
/// draws, fetches or mutates state, so a platform render pump can call
/// it every display frame and skip `draw` when it answers true, letting
/// the presented frame stand and resuming on any change or on the next
/// pending detail's arrival. Throws when the view is detached.
[Throws=SdkError]
boolean frame_settled(MapCamera camera);
};
/// One presented frame's tile and overlay accounting, mirroring how the
/// surface renderer resolves its scheduler verdicts. Every requested tile
/// lands in exactly one of the four readiness counts, so
/// `present + fallback + deferred + unavailable` always equals `requested`.
/// The four readiness names mirror the tile scheduler's fetch-state
/// taxonomy (`Present`/`FallsBackTo`/`Deferred`/`Unavailable`).
dictionary TileFrameStatistics {
/// Tile requests the frame resolved (per request; a request listing
/// carrying duplicates costs one fetch but reports per request).
u32 requested_tile_count;
/// Tiles drawn at their own zoom level.
u32 present_tile_count;
/// Tiles drawn from their nearest pooled ancestor.
u32 fallback_tile_count;
/// Tiles whose fetch was deferred by the per-frame budget.
u32 deferred_tile_count;
/// Tiles the provider rejected this frame.
u32 unavailable_tile_count;
/// Clip-space triangles the overlay layer contributed to the frame.
u32 overlay_triangle_count;
};
// ---------------------------------------------------------------------------
// Round-10 contract additions (addition-only; renderer-owned location
// indicator, SDK-owned speech triggers, structured diagnostics).
// ---------------------------------------------------------------------------
/// Which visual form the renderer-owned location layer draws (OQ3's
/// style-selected single layer).
enum LocationIndicatorVisualKind {
/// Plain disc (tracking mode: no course known or no route).
"TrackingDot",
/// Disc plus course-directed heading wedge (navigation follow mode).
"GuidedCourseArrow",
};
/// Style of the location layer; null fields keep the documented
/// `map-rendering` defaults (the overlay-style pattern). The disc and wedge
/// are screen-sized pixel constants (a position marker scales with the
/// screen, not the ground); the accuracy ring is ground-sized.
dictionary LocationIndicatorStyle {
/// Which visual form this layer draws.
LocationIndicatorVisualKind visual_kind;
/// Disc radius in screen pixels; null keeps the default.
double? radius_pixels;
/// Disc fill color; null keeps the default.
OverlayColor? rgba_color;
/// Accuracy ring drawn around the disc (shown only when a fix carries a
/// reported accuracy); null keeps the default (shown whenever the fix
/// reports one that reaches the minimum draw size).
boolean? show_accuracy_ring;
};
/// One frame's location-layer state (the presentable subset of a position
/// fix): position, optional course over ground, optional horizontal accuracy.
dictionary LocationIndicatorState {
/// Fix position.
GeographicCoordinate coordinate;
/// Course over ground in degrees clockwise from true north; null when
/// the source does not report one (the wedge renders only when set and
/// the style carries `GuidedCourseArrow`).
double? course_heading_degrees;
/// Horizontal accuracy radius in meters; the accuracy ring renders when
/// set and the style shows rings.
double? horizontal_accuracy_meters;
};
/// When a spoken-trigger event fires and what carries it (OQ5). The
/// utterance text is the contract's composed speech text verbatim — the SDK
/// owns when a line is newsworthy, the platform owns playback and synthesis.
dictionary SpeechInstruction {
/// The verbatim speech text (from the contract's composition; never
/// rewritten by the trigger policy).
string utterance_speech_text;
/// The live distance to the maneuver in meters at emission time.
double trigger_distance_meters;
/// Round 11 (Ferrostar B4): the utterance identity — a stable monotonic
/// id composed at emission, strictly increasing over every line the
/// controller has emitted since construction and never reused.
/// Identical text re-emitted is a new id; dedup and flush decisions stay
/// at the platform speaker.
u64 utterance_id;
/// What triggered this line (a crossed band, the immediate band, an
/// off-route recovery, or the trip's arrival).
SpeechInstructionKind instruction_kind;
};
/// What triggered a `SpeechInstruction`.
enum SpeechInstructionKind {
/// A trigger band was crossed this fix (the moderate or far threshold).
"ManeuverApproach",
/// The immediate band: the maneuver is imminent.
"ImmediateManeuver",
/// Guidance just recovered from off-route (guidance resumed).
"OffRouteRecovery",
/// The trip arrived.
"Arrival",
};
/// Severity of an SDK diagnostic event, mirroring the `tracing` levels the
/// crates log under.
enum SdkLogSeverity {
/// Very fine-grained frame-path instrumentation (GPU-hot path logs live
/// here, only when a listener subscribed at this severity).
"Trace",
/// Fine-grained internal state transitions.
"Debug",
/// Coarse-grained progress (the default minimum for a subscription).
"Info",
/// A problem this event explains.
"Warn",
/// A failure this event explains.
"Error",
};
/// One structured SDK internal log event, as delivered to a foreign
/// diagnostics listener or read back by a flush. Coordinates in event
/// messages are rounded at the sink (never the API key, and never raw
/// coordinates at full precision).
dictionary DiagnosticLogEvent {
/// Wall-clock milliseconds since the Unix epoch, as the ring recorded
/// the event.
u64 timestamp_milliseconds_since_epoch;
/// The event's severity.
SdkLogSeverity severity;
/// The named per-subsystem target that logged it, e.g. "tile_fetch".
string subsystem_target;
/// The one-line already-scrubbed message.
string message_line;
};
/// Diagnostics contract implemented by foreign code, receiving the SDK's
/// structured internal log events fire-and-forget (the Mozilla
/// application-services ADR 0009 sink pattern): the SDK copies an event
/// across the FFI and moves on immediately without awaiting the listener —
/// a slow listener can never slow the events it observes, and a frame-path
/// event never blocks on a foreign callback.
[Trait, WithForeign]
interface SdkDiagnosticEventListener {
/// Receives one structured event (severity-gated at the subscription).
void on_diagnostic_event(DiagnosticLogEvent event);
};
// ---------------------------------------------------------------------------
// Round-12 provider-failure diagnostics (addition-only; the owner ruling of
// 2026-10-29 on unclassified foreign provider failures). Foreign
// implementations of the `[Trait, WithForeign]` provider contracts fail in
// two shapes: the typed shape (the foreign code threw one of the contract's
// `SdkError` variants, which round-trips one-to-one) and the unclassified
// shape (the foreign code failed some other way, which the Rust conversion
// reports under `ProviderRequestRejected` with its verbatim reason — the
// honest but thin detail the seam exists for). Every failed foreign-callback
// provider call is recorded on the seam with the contract and the callback
// method it occurred on, a timestamp, the classification, and the verbatim
// failure text; `MapSdk::latest_provider_failure_diagnostics` reads the
// bounded ring back.
//
// Honest limitation of the classification: the unclassified shape is
// recognized by the unclassified-callback conversion's verbatim detail text
// (one Rust-side constant shared by the conversion and the adapter-side
// classifier), so a foreign implementation that itself throws
// `ProviderRequestRejected` carrying exactly that conversion phrasing is
// indistinguishable and lands as unclassified. Richer typed payloads (a
// provider status code, a retriable flag) would need a channel the frozen
// provider traits cannot carry without a contract change; until one exists
// the foreign side's only input to the seam is the failure text it throws,
// which the entries carry verbatim — no data is parsed or invented.
// ---------------------------------------------------------------------------
/// Which shape of foreign provider failure a diagnostic captured
/// (`ProviderFailureDiagnostic::failure_classification`).
enum ProviderFailureClassification {
/// The foreign implementation failed without a mapped error (an
/// exception the contract does not spell): the failure text is the
/// uniffi callback machinery's verbatim reason for it.
"UnclassifiedCallbackFailure",
/// The foreign implementation threw one of the contract's `SdkError`
/// variants, which round-trips typed through the seam: the failure text
/// is the thrown binding error's verbatim display form.
"MappedContractError",
};
/// One failed foreign-callback provider call, as retained in
/// `MapSdk::latest_provider_failure_diagnostics`'s bounded per-object ring.
/// Everything carried here is knowable at the adapter boundary that bridges
/// the foreign implementation onto the native provider registry; no data is
/// invented beyond it.
dictionary ProviderFailureDiagnostic {
/// Wall-clock milliseconds since the Unix epoch, as the seam recorded
/// the failure.
u64 timestamp_milliseconds_since_epoch;
/// Which failure shape this entry captured.
ProviderFailureClassification failure_classification;
/// The provider contract the foreign code implements, e.g.
/// `GeocodingProvider`.
string provider_contract_name;
/// The contract method whose call failed, e.g. `forward_geocode`.
string provider_method_name;
/// The verbatim failure text: the uniffi callback machinery's reason for
/// the unclassified shape, or the thrown binding error's display form
/// for the mapped shape.
string failure_detail;
};