From ee8088a230d5ac342fb6ed9051df5c6acd54331a Mon Sep 17 00:00:00 2001 From: escapedcat Date: Wed, 12 Aug 2026 14:16:55 +0200 Subject: [PATCH 1/5] Add ts-rs and export the SearchedPlace type Co-Authored-By: Claude Fable 5 --- .gitignore | 3 ++- Cargo.lock | 23 +++++++++++++++++++++++ Cargo.toml | 5 +++++ src/rest/v4/places.rs | 24 +++++++++++++++++++++++- 4 files changed, 53 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index c41cc9e3..cbd10cf0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -/target \ No newline at end of file +/target +/bindings diff --git a/Cargo.lock b/Cargo.lock index 874486d4..97957ebd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -696,6 +696,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "ts-rs", "url", "uuid", ] @@ -4638,6 +4639,28 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "ts-rs" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756050066659291d47a554a9f558125db17428b073c5ffce1daf5dcb0f7231d8" +dependencies = [ + "thiserror 2.0.18", + "ts-rs-macros", +] + +[[package]] +name = "ts-rs-macros" +version = "12.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d90eea51bc7988ef9e674bf80a85ba6804739e535e9cab48e4bb34a8b652aa" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "termcolor", +] + [[package]] name = "typenum" version = "1.20.1" diff --git a/Cargo.toml b/Cargo.toml index 377e9b7b..5ad18ea4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -124,3 +124,8 @@ nostr = { version = "0.44.3", default-features = false, features = ["std"] } # Used to query Bitcoin wallet balances from xpubs via a public Electrum server # https://github.com/bitcoindevkit/rust-electrum-client/releases electrum-client = { version = "0.25.0", default-features = false, features = ["use-rustls"] } + +# Derives TypeScript definitions from response structs so btcmap.org can +# generate its API types from this crate (dev tool; no runtime use) +# https://github.com/Aleph-Alpha/ts-rs/blob/main/CHANGELOG.md +ts-rs = "12.0.1" diff --git a/src/rest/v4/places.rs b/src/rest/v4/places.rs index 9047b1a9..1b0dd388 100644 --- a/src/rest/v4/places.rs +++ b/src/rest/v4/places.rs @@ -76,53 +76,75 @@ pub struct SearchArgs { tag_value: Option, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct SearchedPlace { + #[ts(type = "number")] pub id: i64, pub lat: f64, pub lon: f64, pub icon: String, pub name: String, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub address: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub opening_hours: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional, type = "number")] pub comments: Option, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub updated_at: OffsetDateTime, #[serde(with = "time::serde::rfc3339::option")] + #[ts(type = "string | null")] pub verified_at: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub osm_id: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub phone: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub website: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub twitter: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub facebook: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub instagram: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub line: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub email: Option, #[serde(skip_serializing_if = "Option::is_none")] #[serde(with = "time::serde::rfc3339::option")] + #[ts(optional, type = "string")] pub boosted_until: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub required_app_url: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub description: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub image: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub payment_provider: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional, type = "Record")] pub localized_name: Option>, } From 4920b195d78c962487fd3e09e77ad085347166af Mon Sep 17 00:00:00 2001 From: escapedcat Date: Wed, 12 Aug 2026 14:19:19 +0200 Subject: [PATCH 2/5] Add an exported Place type for the dynamic fields responses Co-Authored-By: Claude Fable 5 --- src/rest/v4/places.rs | 89 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/rest/v4/places.rs b/src/rest/v4/places.rs index 1b0dd388..d8658012 100644 --- a/src/rest/v4/places.rs +++ b/src/rest/v4/places.rs @@ -148,6 +148,78 @@ pub struct SearchedPlace { pub localized_name: Option>, } +/// Type-export-only description of the dynamic place responses. +/// `GET /v4/places` and `GET /v4/places/{id}` build their JSON at runtime +/// from the `fields` query param, so every field except `id` is optional. +/// Fields requested via the `osm:` passthrough appear as extra keys and +/// cannot be typed statically. Kept in sync with `service::element::TAGS` by +/// `place_type_covers_all_generate_tags_fields`. +#[derive(ts_rs::TS)] +#[ts(export)] +#[allow(dead_code)] +pub struct Place { + #[ts(type = "number")] + pub id: i64, + #[ts(optional)] + pub osm_id: Option, + #[ts(optional)] + pub osm_url: Option, + #[ts(optional)] + pub osm_edit_url: Option, + #[ts(optional)] + pub lat: Option, + #[ts(optional)] + pub lon: Option, + #[ts(optional)] + pub name: Option, + #[ts(optional)] + pub address: Option, + #[ts(optional)] + pub icon: Option, + #[ts(optional)] + pub phone: Option, + #[ts(optional)] + pub website: Option, + #[ts(optional)] + pub twitter: Option, + #[ts(optional)] + pub facebook: Option, + #[ts(optional)] + pub instagram: Option, + #[ts(optional)] + pub line: Option, + #[ts(optional)] + pub email: Option, + #[ts(optional)] + pub opening_hours: Option, + #[ts(optional)] + pub boosted_until: Option, + #[ts(optional)] + pub required_app_url: Option, + #[ts(optional)] + pub created_at: Option, + #[ts(optional)] + pub updated_at: Option, + #[ts(optional)] + pub deleted_at: Option, + #[ts(optional)] + pub verified_at: Option, + #[ts(optional, type = "number")] + pub comments: Option, + #[ts(optional)] + pub description: Option, + #[ts(optional)] + pub image: Option, + #[ts(optional)] + pub payment_provider: Option, + #[ts(optional)] + pub telegram: Option, + #[ts(optional, type = "Record")] + pub localized_name: Option>, + #[ts(optional, type = "Record")] + pub localized_opening_hours: Option>, +} + #[get("/search")] pub async fn search(args: Query, pool: Data) -> Res> { let lat = args.lat.unwrap_or(0.0); @@ -496,6 +568,23 @@ pub async fn delete_saved(auth: Auth, path: Path, pool: Data) -> Ok(actix_web::web::Json(saved_places)) } +// Separate module: `mod test` below imports actix's `#[test]` macro, which +// only accepts async fns; this is a plain sync test. +#[cfg(test)] +mod place_type_test { + #[test] + fn place_type_covers_all_generate_tags_fields() { + let decl = ::decl(&ts_rs::Config::default()); + for tag in crate::service::element::TAGS { + assert!( + decl.contains(tag), + "Place is missing `{tag}` — TAGS gained a field; add it to the Place struct" + ); + } + assert!(decl.contains("id")); + } +} + #[cfg(test)] mod test { use crate::db::main::area::schema::Area; From b0f9e75ab2816747f5f72bd31b35542b6cada5cd Mon Sep 17 00:00:00 2001 From: escapedcat Date: Wed, 12 Aug 2026 14:23:28 +0200 Subject: [PATCH 3/5] Export TypeScript types for the v4 structs btcmap.org consumes Co-Authored-By: Claude Fable 5 --- src/rest/v4/activity.rs | 11 ++++++++++- src/rest/v4/areas.rs | 8 ++++++-- src/rest/v4/communities.rs | 6 +++++- src/rest/v4/countries.rs | 6 +++++- src/rest/v4/dashboard.rs | 13 +++++++++++-- src/rest/v4/invoices.rs | 3 ++- src/rest/v4/nostr.rs | 3 ++- src/rest/v4/place_boosts.rs | 13 ++++++++++--- src/rest/v4/place_comments.rs | 18 ++++++++++++++---- src/rest/v4/place_issues.rs | 17 ++++++++++++++--- src/rest/v4/places.rs | 21 ++++++++++++++++++--- src/rest/v4/search.rs | 17 +++++++++++++---- src/rest/v4/top_editors.rs | 8 +++++++- src/rest/v4/users.rs | 34 ++++++++++++++++++++++++---------- 14 files changed, 141 insertions(+), 37 deletions(-) diff --git a/src/rest/v4/activity.rs b/src/rest/v4/activity.rs index aefae7ee..91ce594c 100644 --- a/src/rest/v4/activity.rs +++ b/src/rest/v4/activity.rs @@ -36,24 +36,33 @@ pub struct GetActivityArgs { places: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct ActivityItem { pub r#type: String, + #[ts(type = "number")] pub place_id: i64, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub place_name: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional, type = "number")] pub osm_user_id: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub osm_user_name: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub osm_user_tip: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub comment: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional, type = "number")] pub duration_days: Option, pub image: String, #[serde(with = "time::serde::rfc3339", rename = "date")] + #[ts(type = "string")] pub created_at: OffsetDateTime, } diff --git a/src/rest/v4/areas.rs b/src/rest/v4/areas.rs index 7b54a187..e93377e8 100644 --- a/src/rest/v4/areas.rs +++ b/src/rest/v4/areas.rs @@ -22,8 +22,10 @@ pub struct SearchArgs { pub r#type: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct AreaSearchResult { + #[ts(type = "number")] pub id: i64, pub name: String, pub r#type: String, @@ -99,8 +101,10 @@ pub async fn get(args: Query, pool: Data) -> Res, + #[ts(type = "number")] pub places_total: i64, + #[ts(type = "number")] pub places_verified_1y: i64, pub grade: i32, } diff --git a/src/rest/v4/countries.rs b/src/rest/v4/countries.rs index 76c0d3c4..2b492e61 100644 --- a/src/rest/v4/countries.rs +++ b/src/rest/v4/countries.rs @@ -7,13 +7,17 @@ use actix_web::web::Data; use actix_web::web::Json; use serde::Serialize; -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct Country { + #[ts(type = "number")] pub id: i64, pub alias: String, pub name: String, pub icon: Option, + #[ts(type = "number")] pub places_total: i64, + #[ts(type = "number")] pub places_verified_1y: i64, pub grade: i32, } diff --git a/src/rest/v4/dashboard.rs b/src/rest/v4/dashboard.rs index 3380ca7e..5629cabc 100644 --- a/src/rest/v4/dashboard.rs +++ b/src/rest/v4/dashboard.rs @@ -10,22 +10,31 @@ use actix_web::{ use serde::Serialize; use time::{Duration, OffsetDateTime}; -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct Dashboard { + #[ts(type = "number")] pub total_merchants: i64, pub total_merchants_chart: Vec, + #[ts(type = "number")] pub verified_merchants_1y: i64, pub verified_merchants_1y_chart: Vec, + #[ts(type = "number")] pub total_exchanges: i64, pub total_exchanges_chart: Vec, + #[ts(type = "number")] pub verified_exchanges_1y: i64, + #[ts(type = "number")] pub total_areas: i64, + #[ts(type = "number")] pub verified_areas_1y: i64, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct ChartEntry { pub date: String, + #[ts(type = "number")] pub value: i64, } diff --git a/src/rest/v4/invoices.rs b/src/rest/v4/invoices.rs index 71999da8..9b36fb99 100644 --- a/src/rest/v4/invoices.rs +++ b/src/rest/v4/invoices.rs @@ -11,7 +11,8 @@ use actix_web::web::Json; use actix_web::web::Path; use serde::Serialize; -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "Invoice")] pub struct GetByIdRes { id: String, status: String, diff --git a/src/rest/v4/nostr.rs b/src/rest/v4/nostr.rs index 94561c74..9cbc6388 100644 --- a/src/rest/v4/nostr.rs +++ b/src/rest/v4/nostr.rs @@ -11,7 +11,8 @@ use names::Name; use serde::{Deserialize, Serialize}; use uuid::Uuid; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct AuthNostrResponse { pub token: String, pub username: String, diff --git a/src/rest/v4/place_boosts.rs b/src/rest/v4/place_boosts.rs index 8dfd196a..f032cae3 100644 --- a/src/rest/v4/place_boosts.rs +++ b/src/rest/v4/place_boosts.rs @@ -13,10 +13,14 @@ use actix_web::HttpRequest; use serde::Deserialize; use serde::Serialize; -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PlaceBoostQuote")] pub struct Quote { + #[ts(type = "number")] pub quote_30d_sat: i64, + #[ts(type = "number")] pub quote_90d_sat: i64, + #[ts(type = "number")] pub quote_365d_sat: i64, } @@ -29,13 +33,16 @@ pub async fn get_quote(conf: Data) -> RestResult { })) } -#[derive(Deserialize)] +#[derive(Deserialize, ts_rs::TS)] +#[ts(export, rename = "PostPlaceBoostArgs")] pub struct PostArgs { pub place_id: String, + #[ts(type = "number")] pub days: i64, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PostPlaceBoostResponse")] pub struct PostResponse { pub invoice_id: String, pub invoice: String, diff --git a/src/rest/v4/place_comments.rs b/src/rest/v4/place_comments.rs index 14bd2e00..5f552a52 100644 --- a/src/rest/v4/place_comments.rs +++ b/src/rest/v4/place_comments.rs @@ -39,17 +39,23 @@ const fn default_include_deleted() -> bool { false } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PlaceCommentListItem")] pub struct Item { + #[ts(type = "number")] pub id: i64, + #[ts(type = "number")] pub place_id: i64, pub text: String, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub updated_at: OffsetDateTime, #[serde(skip_serializing_if = "Option::is_none")] #[serde(with = "time::serde::rfc3339::option")] + #[ts(optional, type = "string")] pub deleted_at: Option, } @@ -91,8 +97,10 @@ pub async fn get_by_id(id: Path, pool: Data) -> Result .map(Into::into) } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PlaceCommentQuote")] pub struct Quote { + #[ts(type = "number")] pub quote_sat: i64, } @@ -103,13 +111,15 @@ pub async fn get_quote(conf: Data) -> RestResult { })) } -#[derive(Deserialize)] +#[derive(Deserialize, ts_rs::TS)] +#[ts(export, rename = "PostPlaceCommentArgs")] pub struct PostArgs { pub place_id: String, pub comment: String, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PostPlaceCommentResponse")] pub struct PostResponse { pub invoice_id: String, pub invoice: String, diff --git a/src/rest/v4/place_issues.rs b/src/rest/v4/place_issues.rs index e9d0b546..4b47bca6 100644 --- a/src/rest/v4/place_issues.rs +++ b/src/rest/v4/place_issues.rs @@ -19,15 +19,19 @@ pub struct GetArgs { offset: Option, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct PlaceIssuesRes { + #[ts(type = "number")] pub total_issues: i64, pub requested_issues: Vec, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PlaceIssueSummary")] pub struct ResItem { pub element_osm_type: String, + #[ts(type = "number")] pub element_osm_id: i64, pub element_name: String, pub issue_code: String, @@ -44,18 +48,25 @@ impl From for ResItem { } } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PlaceIssue")] pub struct Issue { + #[ts(type = "number")] pub id: i64, + #[ts(type = "number")] pub place_id: i64, pub code: String, + #[ts(type = "number")] pub severity: i64, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub updated_at: OffsetDateTime, #[serde(skip_serializing_if = "Option::is_none")] #[serde(with = "time::serde::rfc3339::option")] + #[ts(optional, type = "string")] pub deleted_at: Option, } diff --git a/src/rest/v4/places.rs b/src/rest/v4/places.rs index d8658012..7bd3a400 100644 --- a/src/rest/v4/places.rs +++ b/src/rest/v4/places.rs @@ -373,11 +373,14 @@ pub async fn get_by_id( }) } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "PlaceComment")] pub struct Comment { + #[ts(type = "number")] pub id: i64, pub text: String, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub created_at: OffsetDateTime, } @@ -405,18 +408,25 @@ pub async fn get_by_id_comments(id: Path, pool: Data) -> Res, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub user_name: Option, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub user_tip: Option, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub updated_at: OffsetDateTime, } @@ -451,14 +461,19 @@ pub async fn get_by_id_activity(id: Path, pool: Data) -> Res, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub created_at: OffsetDateTime, #[serde(with = "time::serde::rfc3339")] + #[ts(type = "string")] pub updated_at: OffsetDateTime, } diff --git a/src/rest/v4/search.rs b/src/rest/v4/search.rs index 0b608f44..61041c1f 100644 --- a/src/rest/v4/search.rs +++ b/src/rest/v4/search.rs @@ -38,27 +38,33 @@ fn has_next_page(offset: i64, limit: i64, total: i64) -> bool { next_offset < total && next_offset <= MAX_OFFSET } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct SearchedArea { + #[ts(type = "number")] pub id: i64, pub name: String, #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub alias: Option, /// `[west, south, east, north]`. Absent when the area has no bbox of its own. #[serde(skip_serializing_if = "Option::is_none")] + #[ts(optional)] pub bbox: Option<[f64; 4]>, } /// `SearchedPlace` is boxed because it is an order of magnitude larger than /// `SearchedArea`, and clippy's `large_enum_variant` would otherwise fire. -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] #[serde(tag = "type", rename_all = "snake_case")] pub enum SearchResult { Area(SearchedArea), Place(Box), } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct SearchResponse { pub results: Vec, pub total_count: u32, @@ -67,9 +73,12 @@ pub struct SearchResponse { pub pagination: PaginationInfo, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct PaginationInfo { + #[ts(type = "number")] pub offset: i64, + #[ts(type = "number")] pub limit: i64, pub total: u32, } diff --git a/src/rest/v4/top_editors.rs b/src/rest/v4/top_editors.rs index dd5a5aa1..3c5f0098 100644 --- a/src/rest/v4/top_editors.rs +++ b/src/rest/v4/top_editors.rs @@ -20,14 +20,20 @@ pub struct GetTopEditorsArgs { limit: Option, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct TopEditor { + #[ts(type = "number")] pub id: i64, pub name: String, pub avatar_url: Option, + #[ts(type = "number")] pub total_edits: i64, + #[ts(type = "number")] pub places_created: i64, + #[ts(type = "number")] pub places_updated: i64, + #[ts(type = "number")] pub places_deleted: i64, pub tip_url: Option, } diff --git a/src/rest/v4/users.rs b/src/rest/v4/users.rs index 1d11f3ee..44ff30ac 100644 --- a/src/rest/v4/users.rs +++ b/src/rest/v4/users.rs @@ -24,20 +24,26 @@ use serde::Deserialize; use serde::Serialize; use uuid::Uuid; -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct SavedPlace { + #[ts(type = "number")] pub id: i64, pub name: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct SavedArea { + #[ts(type = "number")] pub id: i64, pub name: String, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct MeResponse { + #[ts(type = "number")] pub id: i64, pub name: String, pub roles: Vec, @@ -92,14 +98,17 @@ pub async fn me(auth: Auth, pool: Data) -> Result, Re })) } -#[derive(Deserialize)] +#[derive(Deserialize, ts_rs::TS)] +#[ts(export, rename = "CreateUserArgs")] pub struct PostArgs { pub name: Option, pub password: String, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export, rename = "CreateUserResponse")] pub struct PostResponse { + #[ts(type = "number")] pub id: i64, pub name: String, pub roles: Vec, @@ -134,18 +143,21 @@ pub async fn post( })) } -#[derive(Deserialize)] +#[derive(Deserialize, ts_rs::TS)] +#[ts(export)] pub struct CreateTokenArgs { pub label: Option, } -#[derive(Serialize)] +#[derive(Serialize, ts_rs::TS)] +#[ts(export)] pub struct CreateTokenResponse { pub token: String, pub user: MeResponse, } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, ts_rs::TS)] +#[ts(export)] pub struct ChangePasswordArgs { pub old_password: String, pub new_password: String, @@ -174,7 +186,8 @@ pub async fn change_password( Ok(Json(())) } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, ts_rs::TS)] +#[ts(export)] pub struct UpdateUsernameArgs { pub username: String, } @@ -192,7 +205,8 @@ pub async fn update_username( Ok(Json(MeResponse::from(&updated_user))) } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, ts_rs::TS)] +#[ts(export)] pub struct NostrIdentityResponse { /// Bech32 npub (`npub1...`) currently linked to the account, or `null`. pub npub: Option, From 2d6f6e130dd02cfe20df50359aadc766c19e8db0 Mon Sep 17 00:00:00 2001 From: escapedcat Date: Wed, 12 Aug 2026 14:23:47 +0200 Subject: [PATCH 4/5] Add a devtools subcommand to export TypeScript bindings Co-Authored-By: Claude Fable 5 --- devtools | 11 +++++++++++ devtools_completion.bash | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/devtools b/devtools index 1d3f7907..1e2c2f4b 100755 --- a/devtools +++ b/devtools @@ -76,6 +76,17 @@ case "$1" in rm -f /tmp/schema_gen.db ;; + export-ts-types) + # Export the ts-rs TypeScript bindings into a btcmap.org checkout + # (or any target directory). The frontend commits the result. + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + cd "$script_dir" + target="${2:-../btcmap.org/src/types/btcmap-api}" + mkdir -p "$target" + TS_RS_EXPORT_DIR="$(cd "$target" && pwd)" cargo test export_bindings \ + && echo "exported TypeScript bindings to $target" + ;; + install-completions) script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" diff --git a/devtools_completion.bash b/devtools_completion.bash index a1400842..be9f0d14 100644 --- a/devtools_completion.bash +++ b/devtools_completion.bash @@ -4,7 +4,7 @@ _devtools() { local cur prev words cword _init_completion || return - local commands="main-db image-db log-db fetch-db fetch-main-db fetch-image-db fetch-log-db deploy gen-main-schema install-completions" + local commands="main-db image-db log-db fetch-db fetch-main-db fetch-image-db fetch-log-db deploy gen-main-schema export-ts-types install-completions" if [[ $cword -eq 1 ]]; then COMPREPLY=($(compgen -W "$commands" -- "$cur")) From 6627d37eed79632595439c4a8fc5bb98cf6e2d22 Mon Sep 17 00:00:00 2001 From: escapedcat Date: Wed, 12 Aug 2026 15:37:10 +0200 Subject: [PATCH 5/5] Commit the TypeScript bindings and enforce freshness in CI The frontend fetches bindings/ from GitHub instead of needing a local checkout with a Rust toolchain. cargo test regenerates the directory, so a CI diff check keeps the committed copy honest. Co-Authored-By: Claude Fable 5 --- .github/workflows/preview.yml | 5 +++++ .gitignore | 1 - AGENTS.md | 18 ++++++++++++++++ README.md | 31 ++++++++++++++++++++++++++++ bindings/ActivityItem.ts | 3 +++ bindings/Area.ts | 3 +++ bindings/AreaSearchResult.ts | 3 +++ bindings/AuthNostrResponse.ts | 3 +++ bindings/ChangePasswordArgs.ts | 3 +++ bindings/ChartEntry.ts | 3 +++ bindings/Community.ts | 3 +++ bindings/Country.ts | 3 +++ bindings/CreateTokenArgs.ts | 3 +++ bindings/CreateTokenResponse.ts | 4 ++++ bindings/CreateUserArgs.ts | 3 +++ bindings/CreateUserResponse.ts | 3 +++ bindings/Dashboard.ts | 4 ++++ bindings/Invoice.ts | 3 +++ bindings/MeResponse.ts | 10 +++++++++ bindings/NostrIdentityResponse.ts | 7 +++++++ bindings/PaginationInfo.ts | 3 +++ bindings/Place.ts | 11 ++++++++++ bindings/PlaceActivity.ts | 3 +++ bindings/PlaceArea.ts | 3 +++ bindings/PlaceBoostQuote.ts | 3 +++ bindings/PlaceComment.ts | 3 +++ bindings/PlaceCommentListItem.ts | 3 +++ bindings/PlaceCommentQuote.ts | 3 +++ bindings/PlaceIssue.ts | 3 +++ bindings/PlaceIssueSummary.ts | 3 +++ bindings/PlaceIssuesRes.ts | 4 ++++ bindings/PostPlaceBoostArgs.ts | 3 +++ bindings/PostPlaceBoostResponse.ts | 3 +++ bindings/PostPlaceCommentArgs.ts | 3 +++ bindings/PostPlaceCommentResponse.ts | 3 +++ bindings/SavedArea.ts | 3 +++ bindings/SavedPlace.ts | 3 +++ bindings/SearchResponse.ts | 5 +++++ bindings/SearchResult.ts | 9 ++++++++ bindings/SearchedArea.ts | 7 +++++++ bindings/SearchedPlace.ts | 3 +++ bindings/TopEditor.ts | 3 +++ bindings/UpdateUsernameArgs.ts | 3 +++ 43 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 bindings/ActivityItem.ts create mode 100644 bindings/Area.ts create mode 100644 bindings/AreaSearchResult.ts create mode 100644 bindings/AuthNostrResponse.ts create mode 100644 bindings/ChangePasswordArgs.ts create mode 100644 bindings/ChartEntry.ts create mode 100644 bindings/Community.ts create mode 100644 bindings/Country.ts create mode 100644 bindings/CreateTokenArgs.ts create mode 100644 bindings/CreateTokenResponse.ts create mode 100644 bindings/CreateUserArgs.ts create mode 100644 bindings/CreateUserResponse.ts create mode 100644 bindings/Dashboard.ts create mode 100644 bindings/Invoice.ts create mode 100644 bindings/MeResponse.ts create mode 100644 bindings/NostrIdentityResponse.ts create mode 100644 bindings/PaginationInfo.ts create mode 100644 bindings/Place.ts create mode 100644 bindings/PlaceActivity.ts create mode 100644 bindings/PlaceArea.ts create mode 100644 bindings/PlaceBoostQuote.ts create mode 100644 bindings/PlaceComment.ts create mode 100644 bindings/PlaceCommentListItem.ts create mode 100644 bindings/PlaceCommentQuote.ts create mode 100644 bindings/PlaceIssue.ts create mode 100644 bindings/PlaceIssueSummary.ts create mode 100644 bindings/PlaceIssuesRes.ts create mode 100644 bindings/PostPlaceBoostArgs.ts create mode 100644 bindings/PostPlaceBoostResponse.ts create mode 100644 bindings/PostPlaceCommentArgs.ts create mode 100644 bindings/PostPlaceCommentResponse.ts create mode 100644 bindings/SavedArea.ts create mode 100644 bindings/SavedPlace.ts create mode 100644 bindings/SearchResponse.ts create mode 100644 bindings/SearchResult.ts create mode 100644 bindings/SearchedArea.ts create mode 100644 bindings/SearchedPlace.ts create mode 100644 bindings/TopEditor.ts create mode 100644 bindings/UpdateUsernameArgs.ts diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index 9bf062f7..a92068f9 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -24,6 +24,11 @@ jobs: - name: Run tests run: cargo test --verbose + # cargo test regenerates bindings/ (ts-rs), so a diff here means the + # committed TypeScript bindings are stale — commit the regenerated files + - name: Check TypeScript bindings are up to date + run: git diff --exit-code bindings/ + - name: Build run: cargo build --release --verbose diff --git a/.gitignore b/.gitignore index cbd10cf0..ea8c4bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /target -/bindings diff --git a/AGENTS.md b/AGENTS.md index 86f8bd23..72513225 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,6 +53,24 @@ cargo test # Run tests ### Rust version The project does not pin its Rust version. Contributors should use a recent stable Rust toolchain with `rustfmt` and `clippy` components installed. +## TypeScript Bindings (`bindings/`) + +Committed, generated TypeScript definitions for the v4 REST types consumed by +the btcmap.org frontend (see the README section "TypeScript bindings" for the +full picture). Rules when working here: + +- Never edit `bindings/*.ts` by hand — they are ts-rs output, regenerated by + every `cargo test` run. CI fails if the committed files don't match the code. +- When you change a struct that derives `ts_rs::TS`, run `cargo test` and + commit the updated `bindings/` files in the same commit. +- New exported structs follow the conventions: `#[ts(type = "number")]` on + i64/u64 fields, `#[ts(type = "string")]` on RFC 3339 timestamp fields, + `#[ts(optional)]` on `Option` fields with `skip_serializing_if`, and + `#[ts(rename = "...")]` when the struct name collides with another module's. + Export is opt-in: only derive on types the frontend actually consumes. +- The all-optional `Place` binding must list every `service::element::TAGS` + entry — the `place_type_covers_all_generate_tags_fields` test enforces it. + ## Code Structure ``` diff --git a/README.md b/README.md index 87da5c9c..ccdd4603 100644 --- a/README.md +++ b/README.md @@ -94,4 +94,35 @@ The `devtools` script provides helper commands for development: | `fetch-log-db` | Fetch only the log database | | `deploy` | Run tests, build release, deploy to production | | `gen-main-schema` | Generate `schema.sql` from migrations | +| `export-ts-types [dir]` | Export the TypeScript bindings to a directory | + +### TypeScript bindings (`bindings/`) + +`bindings/` contains TypeScript definitions for the v4 REST types that +[btcmap.org](https://github.com/teambtcmap/btcmap.org) consumes. They are +**generated, not hand-written**: structs annotated with `#[derive(ts_rs::TS)]` +are exported by [ts-rs](https://github.com/Aleph-Alpha/ts-rs) every time +`cargo test` runs, so the directory always matches the code on your branch and +never goes stale silently — CI fails if a commit leaves it out of date. + +How the pieces fit: + +- **Changing an exported struct?** Run `cargo test` (or + `devtools export-ts-types`), and commit the updated `bindings/` files along + with your change. The diff doubles as a readable record of the API change. +- **Adding a new response type for the frontend?** Add + `#[derive(ts_rs::TS)]` + `#[ts(export)]` to the struct. Conventions: + 64-bit integers get `#[ts(type = "number")]` (JSON transport), RFC 3339 + timestamps get `#[ts(type = "string")]`, and names that repeat across + modules get `#[ts(rename = "...")]` so every binding file is unique. + Export is opt-in per struct — types not meant for third-party use simply + don't get the derive. +- **Consuming the types?** The frontend fetches this directory from GitHub + (`pnpm types:api` in btcmap.org) — no Rust toolchain or checkout of this + repo required. Any other client can do the same. + +The dynamic `GET /v4/places` responses (shaped by the `fields` query param) +are described by the all-optional `Place` type, kept in sync with +`service::element::TAGS` by the `place_type_covers_all_generate_tags_fields` +test. | `install-completions` | Install bash tab completions for devtools | diff --git a/bindings/ActivityItem.ts b/bindings/ActivityItem.ts new file mode 100644 index 00000000..354d59d0 --- /dev/null +++ b/bindings/ActivityItem.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ActivityItem = { type: string, place_id: number, place_name?: string, osm_user_id?: number, osm_user_name?: string, osm_user_tip?: string, comment?: string, duration_days?: number, image: string, date: string, }; diff --git a/bindings/Area.ts b/bindings/Area.ts new file mode 100644 index 00000000..29e4fb77 --- /dev/null +++ b/bindings/Area.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Area = { id: number, name: string, type: string, url_alias: string, icon: string | null, icon_wide: string | null, website_url: string, description: string, }; diff --git a/bindings/AreaSearchResult.ts b/bindings/AreaSearchResult.ts new file mode 100644 index 00000000..2ebca891 --- /dev/null +++ b/bindings/AreaSearchResult.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AreaSearchResult = { id: number, name: string, type: string, url_alias: string, icon: string | null, website_url: string, }; diff --git a/bindings/AuthNostrResponse.ts b/bindings/AuthNostrResponse.ts new file mode 100644 index 00000000..3a4916fb --- /dev/null +++ b/bindings/AuthNostrResponse.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type AuthNostrResponse = { token: string, username: string, npub: string, }; diff --git a/bindings/ChangePasswordArgs.ts b/bindings/ChangePasswordArgs.ts new file mode 100644 index 00000000..d73c4dc5 --- /dev/null +++ b/bindings/ChangePasswordArgs.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ChangePasswordArgs = { old_password: string, new_password: string, }; diff --git a/bindings/ChartEntry.ts b/bindings/ChartEntry.ts new file mode 100644 index 00000000..203e41da --- /dev/null +++ b/bindings/ChartEntry.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ChartEntry = { date: string, value: number, }; diff --git a/bindings/Community.ts b/bindings/Community.ts new file mode 100644 index 00000000..59934288 --- /dev/null +++ b/bindings/Community.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Community = { id: number, alias: string, name: string, icon: string | null, places_total: number, places_verified_1y: number, grade: number, }; diff --git a/bindings/Country.ts b/bindings/Country.ts new file mode 100644 index 00000000..dc1f1374 --- /dev/null +++ b/bindings/Country.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Country = { id: number, alias: string, name: string, icon: string | null, places_total: number, places_verified_1y: number, grade: number, }; diff --git a/bindings/CreateTokenArgs.ts b/bindings/CreateTokenArgs.ts new file mode 100644 index 00000000..ed5fb0bd --- /dev/null +++ b/bindings/CreateTokenArgs.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CreateTokenArgs = { label: string | null, }; diff --git a/bindings/CreateTokenResponse.ts b/bindings/CreateTokenResponse.ts new file mode 100644 index 00000000..b8ce5f23 --- /dev/null +++ b/bindings/CreateTokenResponse.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { MeResponse } from "./MeResponse"; + +export type CreateTokenResponse = { token: string, user: MeResponse, }; diff --git a/bindings/CreateUserArgs.ts b/bindings/CreateUserArgs.ts new file mode 100644 index 00000000..45a64adc --- /dev/null +++ b/bindings/CreateUserArgs.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CreateUserArgs = { name: string | null, password: string, }; diff --git a/bindings/CreateUserResponse.ts b/bindings/CreateUserResponse.ts new file mode 100644 index 00000000..67ee9bcb --- /dev/null +++ b/bindings/CreateUserResponse.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type CreateUserResponse = { id: number, name: string, roles: Array, }; diff --git a/bindings/Dashboard.ts b/bindings/Dashboard.ts new file mode 100644 index 00000000..d7eb9d2d --- /dev/null +++ b/bindings/Dashboard.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ChartEntry } from "./ChartEntry"; + +export type Dashboard = { total_merchants: number, total_merchants_chart: Array, verified_merchants_1y: number, verified_merchants_1y_chart: Array, total_exchanges: number, total_exchanges_chart: Array, verified_exchanges_1y: number, total_areas: number, verified_areas_1y: number, }; diff --git a/bindings/Invoice.ts b/bindings/Invoice.ts new file mode 100644 index 00000000..f59a7d62 --- /dev/null +++ b/bindings/Invoice.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type Invoice = { id: string, status: string, }; diff --git a/bindings/MeResponse.ts b/bindings/MeResponse.ts new file mode 100644 index 00000000..b97271b0 --- /dev/null +++ b/bindings/MeResponse.ts @@ -0,0 +1,10 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SavedArea } from "./SavedArea"; +import type { SavedPlace } from "./SavedPlace"; + +export type MeResponse = { id: number, name: string, roles: Array, saved_places: Array, saved_areas: Array, +/** + * Bech32 npub (`npub1...`) of the Nostr identity linked to this user, + * or `null` when no pubkey is linked. + */ +npub: string | null, }; diff --git a/bindings/NostrIdentityResponse.ts b/bindings/NostrIdentityResponse.ts new file mode 100644 index 00000000..07dcbc78 --- /dev/null +++ b/bindings/NostrIdentityResponse.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type NostrIdentityResponse = { +/** + * Bech32 npub (`npub1...`) currently linked to the account, or `null`. + */ +npub: string | null, }; diff --git a/bindings/PaginationInfo.ts b/bindings/PaginationInfo.ts new file mode 100644 index 00000000..0c0ded93 --- /dev/null +++ b/bindings/PaginationInfo.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PaginationInfo = { offset: number, limit: number, total: number, }; diff --git a/bindings/Place.ts b/bindings/Place.ts new file mode 100644 index 00000000..5ae71800 --- /dev/null +++ b/bindings/Place.ts @@ -0,0 +1,11 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Type-export-only description of the dynamic place responses. + * `GET /v4/places` and `GET /v4/places/{id}` build their JSON at runtime + * from the `fields` query param, so every field except `id` is optional. + * Fields requested via the `osm:` passthrough appear as extra keys and + * cannot be typed statically. Kept in sync with `service::element::TAGS` by + * `place_type_covers_all_generate_tags_fields`. + */ +export type Place = { id: number, osm_id?: string, osm_url?: string, osm_edit_url?: string, lat?: number, lon?: number, name?: string, address?: string, icon?: string, phone?: string, website?: string, twitter?: string, facebook?: string, instagram?: string, line?: string, email?: string, opening_hours?: string, boosted_until?: string, required_app_url?: string, created_at?: string, updated_at?: string, deleted_at?: string, verified_at?: string, comments?: number, description?: string, image?: string, payment_provider?: string, telegram?: string, localized_name?: Record, localized_opening_hours?: Record, }; diff --git a/bindings/PlaceActivity.ts b/bindings/PlaceActivity.ts new file mode 100644 index 00000000..c3032d1f --- /dev/null +++ b/bindings/PlaceActivity.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceActivity = { id: number, type: string, user_id: number | null, user_name?: string, user_tip?: string, created_at: string, updated_at: string, }; diff --git a/bindings/PlaceArea.ts b/bindings/PlaceArea.ts new file mode 100644 index 00000000..e330a479 --- /dev/null +++ b/bindings/PlaceArea.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceArea = { id: number, alias: string, tags: Record, created_at: string, updated_at: string, }; diff --git a/bindings/PlaceBoostQuote.ts b/bindings/PlaceBoostQuote.ts new file mode 100644 index 00000000..6b3126c0 --- /dev/null +++ b/bindings/PlaceBoostQuote.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceBoostQuote = { quote_30d_sat: number, quote_90d_sat: number, quote_365d_sat: number, }; diff --git a/bindings/PlaceComment.ts b/bindings/PlaceComment.ts new file mode 100644 index 00000000..d3949fad --- /dev/null +++ b/bindings/PlaceComment.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceComment = { id: number, text: string, created_at: string, }; diff --git a/bindings/PlaceCommentListItem.ts b/bindings/PlaceCommentListItem.ts new file mode 100644 index 00000000..7779b640 --- /dev/null +++ b/bindings/PlaceCommentListItem.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceCommentListItem = { id: number, place_id: number, text: string, created_at: string, updated_at: string, deleted_at?: string, }; diff --git a/bindings/PlaceCommentQuote.ts b/bindings/PlaceCommentQuote.ts new file mode 100644 index 00000000..33422dd9 --- /dev/null +++ b/bindings/PlaceCommentQuote.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceCommentQuote = { quote_sat: number, }; diff --git a/bindings/PlaceIssue.ts b/bindings/PlaceIssue.ts new file mode 100644 index 00000000..9ab2f967 --- /dev/null +++ b/bindings/PlaceIssue.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceIssue = { id: number, place_id: number, code: string, severity: number, created_at: string, updated_at: string, deleted_at?: string, }; diff --git a/bindings/PlaceIssueSummary.ts b/bindings/PlaceIssueSummary.ts new file mode 100644 index 00000000..b216373f --- /dev/null +++ b/bindings/PlaceIssueSummary.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PlaceIssueSummary = { element_osm_type: string, element_osm_id: number, element_name: string, issue_code: string, }; diff --git a/bindings/PlaceIssuesRes.ts b/bindings/PlaceIssuesRes.ts new file mode 100644 index 00000000..0b2b9851 --- /dev/null +++ b/bindings/PlaceIssuesRes.ts @@ -0,0 +1,4 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PlaceIssueSummary } from "./PlaceIssueSummary"; + +export type PlaceIssuesRes = { total_issues: number, requested_issues: Array, }; diff --git a/bindings/PostPlaceBoostArgs.ts b/bindings/PostPlaceBoostArgs.ts new file mode 100644 index 00000000..ec70ac84 --- /dev/null +++ b/bindings/PostPlaceBoostArgs.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PostPlaceBoostArgs = { place_id: string, days: number, }; diff --git a/bindings/PostPlaceBoostResponse.ts b/bindings/PostPlaceBoostResponse.ts new file mode 100644 index 00000000..1a91a20d --- /dev/null +++ b/bindings/PostPlaceBoostResponse.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PostPlaceBoostResponse = { invoice_id: string, invoice: string, }; diff --git a/bindings/PostPlaceCommentArgs.ts b/bindings/PostPlaceCommentArgs.ts new file mode 100644 index 00000000..076094bb --- /dev/null +++ b/bindings/PostPlaceCommentArgs.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PostPlaceCommentArgs = { place_id: string, comment: string, }; diff --git a/bindings/PostPlaceCommentResponse.ts b/bindings/PostPlaceCommentResponse.ts new file mode 100644 index 00000000..55c428f1 --- /dev/null +++ b/bindings/PostPlaceCommentResponse.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type PostPlaceCommentResponse = { invoice_id: string, invoice: string, }; diff --git a/bindings/SavedArea.ts b/bindings/SavedArea.ts new file mode 100644 index 00000000..60a91d2e --- /dev/null +++ b/bindings/SavedArea.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SavedArea = { id: number, name: string, }; diff --git a/bindings/SavedPlace.ts b/bindings/SavedPlace.ts new file mode 100644 index 00000000..179e30f8 --- /dev/null +++ b/bindings/SavedPlace.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SavedPlace = { id: number, name: string, }; diff --git a/bindings/SearchResponse.ts b/bindings/SearchResponse.ts new file mode 100644 index 00000000..b4ca7fba --- /dev/null +++ b/bindings/SearchResponse.ts @@ -0,0 +1,5 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PaginationInfo } from "./PaginationInfo"; +import type { SearchResult } from "./SearchResult"; + +export type SearchResponse = { results: Array, total_count: number, has_more: boolean, query: string, pagination: PaginationInfo, }; diff --git a/bindings/SearchResult.ts b/bindings/SearchResult.ts new file mode 100644 index 00000000..cd67dc12 --- /dev/null +++ b/bindings/SearchResult.ts @@ -0,0 +1,9 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { SearchedArea } from "./SearchedArea"; +import type { SearchedPlace } from "./SearchedPlace"; + +/** + * `SearchedPlace` is boxed because it is an order of magnitude larger than + * `SearchedArea`, and clippy's `large_enum_variant` would otherwise fire. + */ +export type SearchResult = { "type": "area" } & SearchedArea | { "type": "place" } & SearchedPlace; diff --git a/bindings/SearchedArea.ts b/bindings/SearchedArea.ts new file mode 100644 index 00000000..a2fc2d22 --- /dev/null +++ b/bindings/SearchedArea.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SearchedArea = { id: number, name: string, alias?: string, +/** + * `[west, south, east, north]`. Absent when the area has no bbox of its own. + */ +bbox?: [number, number, number, number], }; diff --git a/bindings/SearchedPlace.ts b/bindings/SearchedPlace.ts new file mode 100644 index 00000000..5a77e45a --- /dev/null +++ b/bindings/SearchedPlace.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type SearchedPlace = { id: number, lat: number, lon: number, icon: string, name: string, address?: string, opening_hours?: string, comments?: number, created_at: string, updated_at: string, verified_at: string | null, osm_id?: string, phone?: string, website?: string, twitter?: string, facebook?: string, instagram?: string, line?: string, email?: string, boosted_until?: string, required_app_url?: string, description?: string, image?: string, payment_provider?: string, localized_name?: Record, }; diff --git a/bindings/TopEditor.ts b/bindings/TopEditor.ts new file mode 100644 index 00000000..19dde8a3 --- /dev/null +++ b/bindings/TopEditor.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type TopEditor = { id: number, name: string, avatar_url: string | null, total_edits: number, places_created: number, places_updated: number, places_deleted: number, tip_url: string | null, }; diff --git a/bindings/UpdateUsernameArgs.ts b/bindings/UpdateUsernameArgs.ts new file mode 100644 index 00000000..b1688af0 --- /dev/null +++ b/bindings/UpdateUsernameArgs.ts @@ -0,0 +1,3 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type UpdateUsernameArgs = { username: string, };