From 0c726158958ed48b6c87ce3e6af49b72dfe0a734 Mon Sep 17 00:00:00 2001
From: Eric Manganaro
Date: Mon, 27 Jul 2026 22:33:48 -0400
Subject: [PATCH 1/5] feat(ui): install portable commerce blocks
---
Cargo.lock | 1032 ++++++++++++++++-
Cargo.toml | 17 +-
apps/coldfront/Cargo.toml | 6 +
crates/coldfront-ui/Cargo.toml | 7 +
crates/coldfront-ui/build.rs | 14 +
crates/coldfront-ui/components.toml | 69 ++
crates/coldfront-ui/src/components.rs | 15 +
crates/coldfront-ui/src/components/badge.rs | 86 ++
crates/coldfront-ui/src/components/button.rs | 161 +++
crates/coldfront-ui/src/components/card.rs | 98 ++
.../src/components/cart_line_item.rs | 122 ++
.../src/components/catalog_filter_bar.rs | 92 ++
crates/coldfront-ui/src/components/input.rs | 33 +
crates/coldfront-ui/src/components/label.rs | 35 +
.../src/components/order_receipt.rs | 110 ++
.../src/components/order_totals.rs | 105 ++
.../src/components/payment_route_selector.rs | 120 ++
.../src/components/payment_status.rs | 161 +++
.../src/components/product_card.rs | 192 +++
.../coldfront-ui/src/components/progress.rs | 59 +
crates/coldfront-ui/src/components/select.rs | 124 ++
.../src/components/shipping_address_form.rs | 249 ++++
crates/coldfront-ui/src/lib.rs | 3 +
crates/coldfront-ui/styles.css | 88 ++
24 files changed, 2963 insertions(+), 35 deletions(-)
create mode 100644 crates/coldfront-ui/build.rs
create mode 100644 crates/coldfront-ui/components.toml
create mode 100644 crates/coldfront-ui/src/components.rs
create mode 100644 crates/coldfront-ui/src/components/badge.rs
create mode 100644 crates/coldfront-ui/src/components/button.rs
create mode 100644 crates/coldfront-ui/src/components/card.rs
create mode 100644 crates/coldfront-ui/src/components/cart_line_item.rs
create mode 100644 crates/coldfront-ui/src/components/catalog_filter_bar.rs
create mode 100644 crates/coldfront-ui/src/components/input.rs
create mode 100644 crates/coldfront-ui/src/components/label.rs
create mode 100644 crates/coldfront-ui/src/components/order_receipt.rs
create mode 100644 crates/coldfront-ui/src/components/order_totals.rs
create mode 100644 crates/coldfront-ui/src/components/payment_route_selector.rs
create mode 100644 crates/coldfront-ui/src/components/payment_status.rs
create mode 100644 crates/coldfront-ui/src/components/product_card.rs
create mode 100644 crates/coldfront-ui/src/components/progress.rs
create mode 100644 crates/coldfront-ui/src/components/select.rs
create mode 100644 crates/coldfront-ui/src/components/shipping_address_form.rs
create mode 100644 crates/coldfront-ui/styles.css
diff --git a/Cargo.lock b/Cargo.lock
index 2006ae4..1288ae1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -64,6 +64,15 @@ version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "anstream"
version = "1.0.0"
@@ -138,23 +147,58 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "async-trait"
+version = "0.1.91"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 3.0.3",
+]
+
+[[package]]
+name = "atoi"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528"
+dependencies = [
+ "num-traits",
+]
+
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+[[package]]
+name = "autocfg"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
+
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64ct"
+version = "1.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
+
[[package]]
name = "bitflags"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
+dependencies = [
+ "serde_core",
+]
[[package]]
name = "block-buffer"
@@ -207,6 +251,12 @@ version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
+[[package]]
+name = "byteorder"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+
[[package]]
name = "bytes"
version = "1.12.1"
@@ -240,6 +290,20 @@ dependencies = [
"rand_core 0.10.1",
]
+[[package]]
+name = "chrono"
+version = "0.4.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "serde",
+ "wasm-bindgen",
+ "windows-link",
+]
+
[[package]]
name = "cipher"
version = "0.4.4"
@@ -308,9 +372,15 @@ dependencies = [
name = "coldfront"
version = "0.1.0"
dependencies = [
+ "async-trait",
+ "chrono",
"coldfront-ui",
+ "serde",
+ "serde_json",
+ "sqlx",
"tokio",
"topcoat",
+ "uuid",
]
[[package]]
@@ -337,6 +407,7 @@ name = "coldfront-ui"
version = "0.1.0"
dependencies = [
"topcoat",
+ "topcoat-blocks",
]
[[package]]
@@ -363,6 +434,12 @@ version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789"
+[[package]]
+name = "const-oid"
+version = "0.9.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+
[[package]]
name = "const-oid"
version = "0.10.2"
@@ -405,6 +482,12 @@ dependencies = [
"url",
]
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -423,6 +506,21 @@ dependencies = [
"libc",
]
+[[package]]
+name = "crc"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d"
+dependencies = [
+ "crc-catalog",
+]
+
+[[package]]
+name = "crc-catalog"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
+
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -432,6 +530,21 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "crossbeam-queue"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
+
[[package]]
name = "crypto-common"
version = "0.1.7"
@@ -467,6 +580,17 @@ version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
+[[package]]
+name = "der"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+dependencies = [
+ "const-oid 0.9.6",
+ "pem-rfc7468",
+ "zeroize",
+]
+
[[package]]
name = "deranged"
version = "0.5.8"
@@ -480,6 +604,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
+ "const-oid 0.9.6",
"crypto-common 0.1.7",
"subtle",
]
@@ -491,7 +616,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.1",
- "const-oid",
+ "const-oid 0.10.2",
"crypto-common 0.2.2",
]
@@ -515,6 +640,21 @@ dependencies = [
"litrs",
]
+[[package]]
+name = "dotenvy"
+version = "0.15.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
+
+[[package]]
+name = "either"
+version = "1.17.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -531,6 +671,27 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "etcetera"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943"
+dependencies = [
+ "cfg-if",
+ "home",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "event-listener"
+version = "5.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2"
+dependencies = [
+ "parking",
+ "pin-project-lite",
+]
+
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -547,12 +708,29 @@ dependencies = [
"miniz_oxide",
]
+[[package]]
+name = "flume"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+ "spin",
+]
+
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+[[package]]
+name = "foldhash"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -575,6 +753,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae"
dependencies = [
"futures-core",
+ "futures-sink",
]
[[package]]
@@ -583,6 +762,34 @@ version = "0.3.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
+[[package]]
+name = "futures-executor"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-intrusive"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f"
+dependencies = [
+ "futures-core",
+ "lock_api",
+ "parking_lot",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a"
+
[[package]]
name = "futures-macro"
version = "0.3.33"
@@ -613,9 +820,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
dependencies = [
"futures-core",
+ "futures-io",
"futures-macro",
"futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"slab",
]
@@ -694,6 +903,17 @@ dependencies = [
"tracing",
]
+[[package]]
+name = "hashbrown"
+version = "0.15.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
+dependencies = [
+ "allocator-api2",
+ "equivalent",
+ "foldhash 0.1.5",
+]
+
[[package]]
name = "hashbrown"
version = "0.17.1"
@@ -702,7 +922,16 @@ checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
dependencies = [
"allocator-api2",
"equivalent",
- "foldhash",
+ "foldhash 0.2.0",
+]
+
+[[package]]
+name = "hashlink"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
+dependencies = [
+ "hashbrown 0.15.5",
]
[[package]]
@@ -711,6 +940,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+[[package]]
+name = "hex"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+
[[package]]
name = "hkdf"
version = "0.12.4"
@@ -729,6 +964,15 @@ dependencies = [
"digest 0.10.7",
]
+[[package]]
+name = "home"
+version = "0.5.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d"
+dependencies = [
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "http"
version = "1.4.2"
@@ -818,6 +1062,30 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
[[package]]
name = "icu_collections"
version = "2.2.0"
@@ -928,7 +1196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
- "hashbrown",
+ "hashbrown 0.17.1",
]
[[package]]
@@ -972,12 +1240,49 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
+
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
+[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
+name = "libredox"
+version = "0.1.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
+dependencies = [
+ "bitflags",
+ "libc",
+ "plain",
+ "redox_syscall 0.9.0",
+]
+
+[[package]]
+name = "libsqlite3-sys"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149"
+dependencies = [
+ "pkg-config",
+ "vcpkg",
+]
+
[[package]]
name = "litemap"
version = "0.8.2"
@@ -990,6 +1295,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
[[package]]
name = "log"
version = "0.4.33"
@@ -1002,6 +1316,16 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8863b587001c1b9a8a4e36008cebc6b3612cb1226fe2de94858e06092687b608"
+[[package]]
+name = "md-5"
+version = "0.10.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
+dependencies = [
+ "cfg-if",
+ "digest 0.10.7",
+]
+
[[package]]
name = "memchr"
version = "2.8.3"
@@ -1029,12 +1353,57 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "num-bigint-dig"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7"
+dependencies = [
+ "lazy_static",
+ "libm",
+ "num-integer",
+ "num-iter",
+ "num-traits",
+ "rand 0.8.7",
+ "smallvec",
+ "zeroize",
+]
+
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
+[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+ "libm",
+]
+
[[package]]
name = "once_cell"
version = "1.21.4"
@@ -1054,47 +1423,118 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
-name = "percent-encoding"
-version = "2.3.2"
+name = "parking"
+version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
-name = "pin-project-lite"
-version = "0.2.17"
+name = "parking_lot"
+version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
[[package]]
-name = "polyval"
-version = "0.6.2"
+name = "parking_lot_core"
+version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
- "cpufeatures 0.2.17",
- "opaque-debug",
- "universal-hash",
+ "libc",
+ "redox_syscall 0.5.18",
+ "smallvec",
+ "windows-link",
]
[[package]]
-name = "potential_utf"
-version = "0.1.5"
+name = "pem-rfc7468"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
dependencies = [
- "zerovec",
+ "base64ct",
]
[[package]]
-name = "powerfmt"
-version = "0.2.0"
+name = "percent-encoding"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
-name = "ppv-lite86"
-version = "0.2.21"
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "pkcs1"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f"
+dependencies = [
+ "der",
+ "pkcs8",
+ "spki",
+]
+
+[[package]]
+name = "pkcs8"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+dependencies = [
+ "der",
+ "spki",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
+name = "polyval"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.2.17",
+ "opaque-debug",
+ "universal-hash",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
@@ -1216,6 +1656,24 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759"
+dependencies = [
+ "bitflags",
+]
+
[[package]]
name = "ref-cast"
version = "1.0.26"
@@ -1250,6 +1708,26 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rsa"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d"
+dependencies = [
+ "const-oid 0.9.6",
+ "digest 0.10.7",
+ "num-bigint-dig",
+ "num-integer",
+ "num-traits",
+ "pkcs1",
+ "pkcs8",
+ "rand_core 0.6.4",
+ "signature",
+ "spki",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "rustls"
version = "0.23.42"
@@ -1297,6 +1775,12 @@ version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
[[package]]
name = "serde"
version = "1.0.229"
@@ -1421,6 +1905,16 @@ dependencies = [
"libc",
]
+[[package]]
+name = "signature"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+dependencies = [
+ "digest 0.10.7",
+ "rand_core 0.6.4",
+]
+
[[package]]
name = "simd-adler32"
version = "0.3.10"
@@ -1438,6 +1932,9 @@ name = "smallvec"
version = "1.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
+dependencies = [
+ "serde",
+]
[[package]]
name = "socket2"
@@ -1449,12 +1946,240 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "spin"
+version = "0.9.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
+name = "spki"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+dependencies = [
+ "base64ct",
+ "der",
+]
+
+[[package]]
+name = "sqlx"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc"
+dependencies = [
+ "sqlx-core",
+ "sqlx-macros",
+ "sqlx-mysql",
+ "sqlx-postgres",
+ "sqlx-sqlite",
+]
+
+[[package]]
+name = "sqlx-core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6"
+dependencies = [
+ "base64",
+ "bytes",
+ "chrono",
+ "crc",
+ "crossbeam-queue",
+ "either",
+ "event-listener",
+ "futures-core",
+ "futures-intrusive",
+ "futures-io",
+ "futures-util",
+ "hashbrown 0.15.5",
+ "hashlink",
+ "indexmap",
+ "log",
+ "memchr",
+ "once_cell",
+ "percent-encoding",
+ "rustls",
+ "serde",
+ "serde_json",
+ "sha2 0.10.9",
+ "smallvec",
+ "thiserror",
+ "tokio",
+ "tokio-stream",
+ "tracing",
+ "url",
+ "uuid",
+ "webpki-roots 0.26.11",
+]
+
+[[package]]
+name = "sqlx-macros"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "sqlx-core",
+ "sqlx-macros-core",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "sqlx-macros-core"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b"
+dependencies = [
+ "dotenvy",
+ "either",
+ "heck",
+ "hex",
+ "once_cell",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_json",
+ "sha2 0.10.9",
+ "sqlx-core",
+ "sqlx-mysql",
+ "sqlx-postgres",
+ "sqlx-sqlite",
+ "syn 2.0.119",
+ "tokio",
+ "url",
+]
+
+[[package]]
+name = "sqlx-mysql"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526"
+dependencies = [
+ "atoi",
+ "base64",
+ "bitflags",
+ "byteorder",
+ "bytes",
+ "chrono",
+ "crc",
+ "digest 0.10.7",
+ "dotenvy",
+ "either",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-util",
+ "generic-array",
+ "hex",
+ "hkdf",
+ "hmac",
+ "itoa",
+ "log",
+ "md-5",
+ "memchr",
+ "once_cell",
+ "percent-encoding",
+ "rand 0.8.7",
+ "rsa",
+ "serde",
+ "sha1",
+ "sha2 0.10.9",
+ "smallvec",
+ "sqlx-core",
+ "stringprep",
+ "thiserror",
+ "tracing",
+ "uuid",
+ "whoami",
+]
+
+[[package]]
+name = "sqlx-postgres"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46"
+dependencies = [
+ "atoi",
+ "base64",
+ "bitflags",
+ "byteorder",
+ "chrono",
+ "crc",
+ "dotenvy",
+ "etcetera",
+ "futures-channel",
+ "futures-core",
+ "futures-util",
+ "hex",
+ "hkdf",
+ "hmac",
+ "home",
+ "itoa",
+ "log",
+ "md-5",
+ "memchr",
+ "once_cell",
+ "rand 0.8.7",
+ "serde",
+ "serde_json",
+ "sha2 0.10.9",
+ "smallvec",
+ "sqlx-core",
+ "stringprep",
+ "thiserror",
+ "tracing",
+ "uuid",
+ "whoami",
+]
+
+[[package]]
+name = "sqlx-sqlite"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea"
+dependencies = [
+ "atoi",
+ "chrono",
+ "flume",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-intrusive",
+ "futures-util",
+ "libsqlite3-sys",
+ "log",
+ "percent-encoding",
+ "serde",
+ "serde_urlencoded",
+ "sqlx-core",
+ "thiserror",
+ "tracing",
+ "url",
+ "uuid",
+]
+
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+[[package]]
+name = "stringprep"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+ "unicode-properties",
+]
+
[[package]]
name = "strsim"
version = "0.11.1"
@@ -1566,6 +2291,21 @@ dependencies = [
"zerovec",
]
+[[package]]
+name = "tinyvec"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
+
[[package]]
name = "tokio"
version = "1.53.1"
@@ -1593,6 +2333,17 @@ dependencies = [
"syn 2.0.119",
]
+[[package]]
+name = "tokio-stream"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+ "tokio",
+]
+
[[package]]
name = "tokio-tungstenite"
version = "0.29.0"
@@ -1719,6 +2470,8 @@ dependencies = [
"topcoat-runtime",
"topcoat-runtime-macro",
"topcoat-session",
+ "topcoat-tailwind",
+ "topcoat-ui-registry",
"topcoat-view",
"topcoat-view-macro",
]
@@ -1741,6 +2494,11 @@ dependencies = [
"topcoat-view",
]
+[[package]]
+name = "topcoat-blocks"
+version = "0.2.0"
+source = "git+https://github.com/superposition/topcoat-blocks.git?rev=b19fe63c7ad7c368ba1592a5120e82ed0f8881c0#b19fe63c7ad7c368ba1592a5120e82ed0f8881c0"
+
[[package]]
name = "topcoat-cookie"
version = "0.4.0"
@@ -1764,7 +2522,7 @@ dependencies = [
"anyhow",
"anymap3",
"boxcar",
- "hashbrown",
+ "hashbrown 0.17.1",
"pin-project-lite",
"tokio",
]
@@ -1839,9 +2597,13 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "438d3a12ff218310785e0ef538ae2c03c0d95102a164159372185f334f1f024e"
dependencies = [
+ "serde",
+ "serde_json",
+ "thiserror",
"topcoat-core",
"topcoat-view",
"topcoat-view-macro",
+ "ureq",
]
[[package]]
@@ -1985,6 +2747,24 @@ dependencies = [
"topcoat-router",
]
+[[package]]
+name = "topcoat-tailwind"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e6776b0ed30e0f92192cc942c4a7ac8d0f1fa197a0ec0094eebf7ede887ee90"
+dependencies = [
+ "sha2 0.11.0",
+ "thiserror",
+ "topcoat-core",
+ "ureq",
+]
+
+[[package]]
+name = "topcoat-ui-registry"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d4f2d4a5ec84adce65f380bb24c2840bff0998d95e3a2d8278e120aa3ce0fb6"
+
[[package]]
name = "topcoat-view"
version = "0.4.0"
@@ -2075,10 +2855,23 @@ version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
+ "log",
"pin-project-lite",
+ "tracing-attributes",
"tracing-core",
]
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
[[package]]
name = "tracing-core"
version = "0.1.36"
@@ -2110,12 +2903,33 @@ version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+[[package]]
+name = "unicode-bidi"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+[[package]]
+name = "unicode-normalization"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
+dependencies = [
+ "tinyvec",
+]
+
+[[package]]
+name = "unicode-properties"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
+
[[package]]
name = "universal-hash"
version = "0.5.1"
@@ -2149,7 +2963,7 @@ dependencies = [
"serde_json",
"ureq-proto",
"utf8-zero",
- "webpki-roots",
+ "webpki-roots 1.0.9",
]
[[package]]
@@ -2207,6 +3021,12 @@ dependencies = [
"wasm-bindgen",
]
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
[[package]]
name = "version_check"
version = "0.9.5"
@@ -2228,6 +3048,12 @@ dependencies = [
"wit-bindgen",
]
+[[package]]
+name = "wasite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
+
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
@@ -2273,6 +3099,15 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "webpki-roots"
+version = "0.26.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
+dependencies = [
+ "webpki-roots 1.0.9",
+]
+
[[package]]
name = "webpki-roots"
version = "1.0.9"
@@ -2282,19 +3117,91 @@ dependencies = [
"rustls-pki-types",
]
+[[package]]
+name = "whoami"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d"
+dependencies = [
+ "libredox",
+ "wasite",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.119",
+]
+
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -2306,34 +3213,67 @@ dependencies = [
"windows-link",
]
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@@ -2346,24 +3286,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
diff --git a/Cargo.toml b/Cargo.toml
index a334eff..4ff6204 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,10 +14,25 @@ rust-version = "1.95"
version = "0.1.0"
[workspace.dependencies]
+async-trait = "0.1"
+chrono = { version = "0.4", features = ["serde"] }
coatcheck = "0.2.0"
coldfront-ui = { path = "crates/coldfront-ui" }
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+sqlx = { version = "0.8", default-features = false, features = [
+ "chrono",
+ "json",
+ "macros",
+ "migrate",
+ "postgres",
+ "runtime-tokio-rustls",
+ "uuid",
+] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
-topcoat = "0.4.0"
+topcoat = { version = "0.4.0", features = ["icon-iconify", "tailwind", "ui"] }
+topcoat-blocks = { git = "https://github.com/superposition/topcoat-blocks.git", rev = "b19fe63c7ad7c368ba1592a5120e82ed0f8881c0" }
+uuid = { version = "1", features = ["serde", "v4"] }
[workspace.lints.rust]
unsafe_code = "forbid"
diff --git a/apps/coldfront/Cargo.toml b/apps/coldfront/Cargo.toml
index aa56a68..8ef3f00 100644
--- a/apps/coldfront/Cargo.toml
+++ b/apps/coldfront/Cargo.toml
@@ -7,9 +7,15 @@ rust-version.workspace = true
publish = false
[dependencies]
+async-trait.workspace = true
+chrono.workspace = true
coldfront-ui.workspace = true
+serde.workspace = true
+serde_json.workspace = true
+sqlx.workspace = true
tokio.workspace = true
topcoat.workspace = true
+uuid.workspace = true
[lints]
workspace = true
diff --git a/crates/coldfront-ui/Cargo.toml b/crates/coldfront-ui/Cargo.toml
index 0a9b585..f234e2a 100644
--- a/crates/coldfront-ui/Cargo.toml
+++ b/crates/coldfront-ui/Cargo.toml
@@ -7,6 +7,13 @@ rust-version.workspace = true
[dependencies]
topcoat.workspace = true
+topcoat-blocks.workspace = true
+
+[build-dependencies]
+topcoat = { version = "0.4.0", default-features = false, features = [
+ "icon-iconify",
+ "tailwind",
+] }
[lints]
workspace = true
diff --git a/crates/coldfront-ui/build.rs b/crates/coldfront-ui/build.rs
new file mode 100644
index 0000000..3667b51
--- /dev/null
+++ b/crates/coldfront-ui/build.rs
@@ -0,0 +1,14 @@
+fn main() {
+ println!("cargo:rerun-if-changed=src");
+ println!("cargo:rerun-if-changed=styles.css");
+
+ topcoat::tailwind::BuildConfig::new()
+ .input("styles.css")
+ .render()
+ .expect("Cold Front component stylesheet builds");
+
+ topcoat::icon::iconify::BuildConfig::new()
+ .icon_set("feather")
+ .stage()
+ .expect("Cold Front component icons stage");
+}
diff --git a/crates/coldfront-ui/components.toml b/crates/coldfront-ui/components.toml
new file mode 100644
index 0000000..d908c66
--- /dev/null
+++ b/crates/coldfront-ui/components.toml
@@ -0,0 +1,69 @@
+# Topcoat UI install state. Managed by `topcoat ui`.
+version = 1
+components_dir = "src/components"
+
+[theme]
+name = "neutral"
+registry = "topcoat"
+hash = "sha256:c7bdcb5ea4ff757611ba616e92178169b262be98155c06dfa2407584d51e7459"
+file = "styles.css"
+
+[registries.topcoat.components.badge]
+hash = "sha256:ee86a6d59d019450aad3274da752d9ad490885d58edf434c56a95f4afdaaa95e"
+file = "src/components/badge.rs"
+
+[registries.topcoat.components.button]
+hash = "sha256:8dcc9609183d0a51138c7a15de06d5525388f25a3958071fab25b96efaa2b160"
+file = "src/components/button.rs"
+
+[registries.topcoat.components.card]
+hash = "sha256:f7baecc0a71b59126d311ac504c36a14fa98d438d18230b807f10f7e976a30b7"
+file = "src/components/card.rs"
+
+[registries.topcoat.components.input]
+hash = "sha256:7769330ae511769c1c50b39c75b7475b676af9072d2156398762cf505a3bf306"
+file = "src/components/input.rs"
+
+[registries.topcoat.components.label]
+hash = "sha256:cd8e7569717c82abad745f0586f03fbc759e6000988a82c8dce15182a409d0d6"
+file = "src/components/label.rs"
+
+[registries.topcoat.components.progress]
+hash = "sha256:4234a867cf267c07e0f31431ff67c530dcfdebe66a4aa2b923be690a8d630262"
+file = "src/components/progress.rs"
+
+[registries.topcoat.components.select]
+hash = "sha256:5d6bc5c890c12fad1b004569212b6bb83fda3a1e2bd5da261225845419dfbc85"
+file = "src/components/select.rs"
+
+[registries.topcoat-blocks.components.cart_line_item]
+hash = "sha256:81a3de7ea8706a0cfb8f35c08e6dd62379109d521f696725c68f4e82ee2bd065"
+file = "src/components/cart_line_item.rs"
+
+[registries.topcoat-blocks.components.catalog_filter_bar]
+hash = "sha256:990de7bf7006f3411888a5990dbca10b85783f166daf371f5ce75b848fb71010"
+file = "src/components/catalog_filter_bar.rs"
+
+[registries.topcoat-blocks.components.order_receipt]
+hash = "sha256:a6c1a5d5f66b7ba104f8bc0b4d39b61614d066849ed1f6f406a865aaca2c6f4e"
+file = "src/components/order_receipt.rs"
+
+[registries.topcoat-blocks.components.order_totals]
+hash = "sha256:668eaa092724bfcb92d521a21c9ced5fcfb8fca2aeac28d27fb15ac8309181f2"
+file = "src/components/order_totals.rs"
+
+[registries.topcoat-blocks.components.payment_route_selector]
+hash = "sha256:5a1071f608986339df78ed4d0d96cba76a4967e6d6ae8cc318aa422ab2c02589"
+file = "src/components/payment_route_selector.rs"
+
+[registries.topcoat-blocks.components.payment_status]
+hash = "sha256:a4b99f945c4db9e4669d9c5b6ae495c00db93cdc95257db64dc78c097d67f3d4"
+file = "src/components/payment_status.rs"
+
+[registries.topcoat-blocks.components.product_card]
+hash = "sha256:27c3ead6008504a769103aef2657d89626d3b60085eb9c9accac110ac320fc90"
+file = "src/components/product_card.rs"
+
+[registries.topcoat-blocks.components.shipping_address_form]
+hash = "sha256:4705242c0f96865efa318349cf2aa8e2070a2f264b26e67c57b223ebc3eff67e"
+file = "src/components/shipping_address_form.rs"
diff --git a/crates/coldfront-ui/src/components.rs b/crates/coldfront-ui/src/components.rs
new file mode 100644
index 0000000..a601f46
--- /dev/null
+++ b/crates/coldfront-ui/src/components.rs
@@ -0,0 +1,15 @@
+pub mod badge;
+pub mod button;
+pub mod card;
+pub mod cart_line_item;
+pub mod catalog_filter_bar;
+pub mod input;
+pub mod label;
+pub mod order_receipt;
+pub mod order_totals;
+pub mod payment_route_selector;
+pub mod payment_status;
+pub mod product_card;
+pub mod progress;
+pub mod select;
+pub mod shipping_address_form;
diff --git a/crates/coldfront-ui/src/components/badge.rs b/crates/coldfront-ui/src/components/badge.rs
new file mode 100644
index 0000000..fd80266
--- /dev/null
+++ b/crates/coldfront-ui/src/components/badge.rs
@@ -0,0 +1,86 @@
+use topcoat::{
+ Result,
+ view::{Attributes, View, class, component, view},
+};
+
+/// The visual style of a [`badge`].
+///
+/// [`Default`] is `BadgeVariant::Primary`, used when no variant is given.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+#[allow(dead_code)]
+pub enum BadgeVariant {
+ /// The primary-filled badge for highlighted statuses.
+ #[default]
+ Primary,
+ /// A muted, tinted fill for neutral statuses.
+ Secondary,
+ /// A hairline-bordered badge on the page background.
+ Outline,
+ /// A destructive-filled badge for errors and warnings.
+ Destructive,
+}
+
+impl BadgeVariant {
+ /// The Tailwind classes for this variant.
+ ///
+ /// Each variant sets its own border color rather than inheriting a
+ /// transparent one from [`BASE`]: with two border-color classes on the
+ /// same element, stylesheet order (not class order) would decide the
+ /// winner.
+ fn classes(self) -> &'static str {
+ match self {
+ Self::Primary => "border-transparent bg-primary text-primary-foreground",
+ Self::Secondary => "border-transparent bg-foreground/5 text-foreground",
+ Self::Outline => "border-border text-foreground",
+ Self::Destructive => "border-transparent bg-destructive text-destructive-foreground",
+ }
+ }
+}
+
+/// The classes shared by every badge, regardless of variant.
+///
+/// Every badge carries a border (colored per variant) so that the `Outline`
+/// variant, which only recolors it, does not change the badge's dimensions.
+const BASE: &str = "inline-flex w-fit shrink-0 items-center justify-center gap-1 rounded-md \
+ border px-2 py-0.5 text-xs font-medium whitespace-nowrap";
+
+/// Builds the full class string for a badge of the given `variant`.
+///
+/// Use it to give badge styling to another element, such as a link:
+///
+/// ```ignore
+/// view! {
+/// "v2.0"
+/// }
+/// ```
+#[must_use]
+pub fn badge_variants(variant: BadgeVariant) -> String {
+ format!("{BASE} {}", variant.classes())
+}
+
+/// A badge component: a small inline pill for statuses, counts, and tags.
+///
+/// The `variant` parameter selects the styling, defaulting to `Primary`. The
+/// `attrs` (such as `class` or `title`) are forwarded to the underlying
+/// ``; a `class` among them is appended to the computed classes. Child
+/// nodes become the badge's content.
+///
+/// ```ignore
+/// view! {
+/// badge(variant: BadgeVariant::Destructive, "Failed")
+/// }
+/// ```
+///
+/// To style another element like a badge, use [`badge_variants`] directly.
+#[component]
+pub async fn badge(
+ #[default] variant: BadgeVariant,
+ #[default] mut attrs: Attributes,
+ #[default] child: View,
+) -> Result {
+ view! {
+
+ (child)
+
+ }
+}
diff --git a/crates/coldfront-ui/src/components/button.rs b/crates/coldfront-ui/src/components/button.rs
new file mode 100644
index 0000000..73a2134
--- /dev/null
+++ b/crates/coldfront-ui/src/components/button.rs
@@ -0,0 +1,161 @@
+use topcoat::{
+ Result,
+ view::{Attributes, View, class, component, view},
+};
+
+/// The visual style of a [`button`].
+///
+/// [`Default`] is `ButtonVariant::Primary`, used when no variant is given.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+#[allow(dead_code)]
+pub enum ButtonVariant {
+ /// The primary-filled button for the main action.
+ #[default]
+ Primary,
+ /// A muted, tinted fill for secondary actions.
+ Secondary,
+ /// A hairline-bordered button on the page background.
+ Outline,
+ /// No fill until hovered, for toolbars and inline actions.
+ Ghost,
+ /// A destructive-filled button for actions such as deleting data.
+ Destructive,
+}
+
+impl ButtonVariant {
+ /// The Tailwind classes for this variant.
+ ///
+ /// Hover and press states apply the fill or foreground color at reduced
+ /// opacity, so they hold up in both color schemes without `dark:`
+ /// overrides. Every variant with a resting fill or border casts the
+ /// theme's control shadow; `Ghost` is flat until hovered, so it casts
+ /// none.
+ ///
+ /// Each variant sets its own border color rather than inheriting a
+ /// transparent one from [`BASE`]: with two border-color classes on the
+ /// same element, stylesheet order (not class order) would decide the
+ /// winner.
+ fn classes(self) -> &'static str {
+ match self {
+ Self::Primary => {
+ "border-transparent bg-primary text-primary-foreground shadow-xs \
+ hover:bg-primary/90 active:bg-primary/80"
+ }
+ Self::Secondary => {
+ "border-transparent bg-foreground/5 text-foreground shadow-xs \
+ hover:bg-foreground/10 active:bg-foreground/15"
+ }
+ Self::Outline => {
+ "border-border text-foreground shadow-xs hover:bg-foreground/5 \
+ active:bg-foreground/10"
+ }
+ Self::Ghost => {
+ "border-transparent text-foreground hover:bg-foreground/5 active:bg-foreground/10"
+ }
+ Self::Destructive => {
+ "border-transparent bg-destructive text-destructive-foreground shadow-xs \
+ hover:bg-destructive/90 active:bg-destructive/80"
+ }
+ }
+ }
+}
+
+/// The size of a [`button`].
+///
+/// [`Default`] is `ButtonSize::Md`, used when no size is given.
+#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
+#[allow(dead_code)]
+pub enum ButtonSize {
+ /// A compact button.
+ Sm,
+ /// The standard button size.
+ #[default]
+ Md,
+ /// A prominent button.
+ Lg,
+ /// A square button sized for a single icon.
+ Icon,
+}
+
+impl ButtonSize {
+ /// The Tailwind classes for this size.
+ ///
+ /// Each size sets a text size, which also scales any icons inside: the
+ /// `icon` component is `1em` square by default.
+ fn classes(self) -> &'static str {
+ match self {
+ Self::Sm => "h-8 gap-1.5 rounded-md px-3 text-xs",
+ Self::Md => "h-9 gap-2 rounded-lg px-4 text-sm",
+ Self::Lg => "h-10 gap-2 rounded-lg px-5 text-base",
+ Self::Icon => "size-9 rounded-lg text-base",
+ }
+ }
+}
+
+/// The classes shared by every button, regardless of variant or size.
+///
+/// Every button carries a border (colored per variant) so that the `Outline`
+/// variant, which only recolors it, does not change the button's dimensions.
+const BASE: &str = "inline-flex shrink-0 items-center justify-center border \
+ font-medium whitespace-nowrap transition-colors outline-none select-none \
+ focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 \
+ focus-visible:ring-offset-background disabled:pointer-events-none disabled:opacity-50";
+
+/// Builds the full class string for a button of the given `variant` and `size`.
+///
+/// Use it to give button styling to an element that is not a `
- action_link(href: "#briefing", label: "Open mission dossier")
+ action_link(href: "/shop", label: "Enter quartermaster")
action_link(
- href: "#contact",
- label: "Trace the dead drop",
+ href: "#briefing",
+ label: "Open mission dossier",
tone: ActionTone::Quiet,
)
diff --git a/crates/coldfront-ui/src/wallet.js b/crates/coldfront-ui/src/wallet.js
new file mode 100644
index 0000000..4db2c7c
--- /dev/null
+++ b/crates/coldfront-ui/src/wallet.js
@@ -0,0 +1,66 @@
+const trigger = document.querySelector("[data-wallet-connect]");
+const projectId = document
+ .querySelector('meta[name="reown-project-id"]')
+ ?.getAttribute("content");
+
+if (trigger) {
+ if (!projectId) {
+ trigger.disabled = true;
+ trigger.textContent = "Wallet credentials required";
+ trigger.dataset.state = "unconfigured";
+ } else {
+ trigger.addEventListener("click", async () => {
+ trigger.disabled = true;
+ trigger.textContent = "Opening test channel…";
+
+ try {
+ const [{ createAppKit }, { WagmiAdapter }, { SolanaAdapter }, networks] =
+ await Promise.all([
+ import("https://esm.sh/@reown/appkit@1.8.23?bundle"),
+ import("https://esm.sh/@reown/appkit-adapter-wagmi@1.8.23?bundle"),
+ import("https://esm.sh/@reown/appkit-adapter-solana@1.8.23?bundle"),
+ import("https://esm.sh/@reown/appkit@1.8.23/networks?bundle"),
+ ]);
+
+ const enabledNetworks = [
+ networks.sepolia,
+ networks.optimismSepolia,
+ networks.solanaDevnet,
+ ];
+ const modal = createAppKit({
+ adapters: [
+ new WagmiAdapter({
+ projectId,
+ networks: [networks.sepolia, networks.optimismSepolia],
+ }),
+ new SolanaAdapter(),
+ ],
+ networks: enabledNetworks,
+ projectId,
+ metadata: {
+ name: "Cold Front Staging",
+ description: "Fictional espionage field-gear checkout",
+ url: window.location.origin,
+ icons: [],
+ },
+ features: {
+ analytics: false,
+ email: false,
+ socials: [],
+ },
+ enableWalletConnect: true,
+ enableInjected: true,
+ allowUnsupportedChain: false,
+ });
+
+ await modal.open();
+ trigger.disabled = false;
+ trigger.textContent = "Open testnet wallet";
+ } catch {
+ trigger.disabled = false;
+ trigger.textContent = "Wallet channel unavailable";
+ trigger.dataset.state = "error";
+ }
+ });
+ }
+}
From 46eaa41281d8c4ca46e7860809cc486204dd7fa8 Mon Sep 17 00:00:00 2001
From: Eric Manganaro
Date: Mon, 27 Jul 2026 22:34:00 -0400
Subject: [PATCH 4/5] docs: define and package the staging boundary
---
.dockerignore | 11 ++++++
.env.example | 20 +++++++++--
.github/workflows/ci.yml | 1 +
Dockerfile | 21 +++++++++++
README.md | 31 ++++++++++++++--
docs/COMMERCE.md | 76 ++++++++++++++++++++++++++++++++++++++++
docs/README.md | 1 +
railway.toml | 9 +++++
8 files changed, 165 insertions(+), 5 deletions(-)
create mode 100644 .dockerignore
create mode 100644 Dockerfile
create mode 100644 docs/COMMERCE.md
create mode 100644 railway.toml
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..4308b4b
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,11 @@
+target
+dist
+.git
+.tools
+.coatcheck
+node_modules
+playwright-report
+test-results
+.env
+.env.*
+!.env.example
diff --git a/.env.example b/.env.example
index 74b0dcd..ba48aa9 100644
--- a/.env.example
+++ b/.env.example
@@ -1,7 +1,23 @@
+APP_ENV=local
+HOST=127.0.0.1
+PORT=3100
+DATABASE_URL=
+COOKIE_KEY=replace-with-at-least-32-random-characters
+
CLERK_PUBLISHABLE_KEY=pk_test_replace_me
CLERK_SECRET_KEY=sk_test_replace_me
CLERK_ISSUER=https://replace-me.clerk.accounts.dev
-CLERK_AUTHORIZED_PARTIES=http://127.0.0.1:3000
+CLERK_AUTHORIZED_PARTIES=http://127.0.0.1:3100
CLERK_AUDIENCE=
CLERK_JWKS_URL=
-APP_ENV=development
+
+REOWN_PROJECT_ID=
+
+# The first commerce release is test-only. Production mainnet, real postage,
+# and provider-backed tax stay disabled until their acceptance gates pass.
+COMMERCE_MAINNET_ENABLED=false
+USPS_CLIENT_ID=
+USPS_CLIENT_SECRET=
+UPS_CLIENT_ID=
+UPS_CLIENT_SECRET=
+TAX_PROVIDER=
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index ff7ebb3..e9f43da 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,6 +21,7 @@ jobs:
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- run: cargo test --workspace --all-features
- run: node --check crates/coldfront-ui/src/auth.js
+ - run: node --check crates/coldfront-ui/src/wallet.js
coatcheck:
runs-on: ubuntu-latest
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..5842dd0
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,21 @@
+FROM rust:1.97-bookworm AS builder
+
+WORKDIR /workspace
+COPY . .
+RUN cargo install topcoat-cli --version 0.4.0 --locked \
+ && topcoat asset bundle --package coldfront --release
+
+FROM debian:bookworm-slim
+
+RUN apt-get update \
+ && apt-get install --yes --no-install-recommends ca-certificates \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY --from=builder /workspace/target/release/coldfront /usr/local/bin/coldfront
+COPY --from=builder /workspace/target/assets /usr/local/bin/assets
+
+ENV HOST=0.0.0.0
+ENV APP_ENV=staging
+EXPOSE 3000
+
+CMD ["coldfront"]
diff --git a/README.md b/README.md
index 49bb230..fa24834 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Cold Front
-Cold Front is a game website built in Rust with
+Cold Front is a fictional espionage game and field-gear storefront built in Rust with
[Topcoat](https://github.com/tokio-rs/topcoat) and Tokio. It presents a
fictional Arctic espionage mission: intercept the courier, verify every
transmission, and reach extraction before satellite cover returns.
@@ -22,6 +22,13 @@ standalone [`coatcheck`](https://github.com/superposition/coatcheck) crate;
Coatcheck stays product-neutral while Cold Front owns its components, stories,
and visual language.
+The commerce surfaces install provider-neutral source from the separate
+[`topcoat-blocks`](https://github.com/superposition/topcoat-blocks) registry.
+Cold Front owns all product data, pricing, cart state, payment policy,
+fulfillment decisions, and gritty styling. See
+[`docs/COMMERCE.md`](docs/COMMERCE.md) for the staging boundary and release
+gates.
+
## Start both servers
```sh
@@ -36,6 +43,7 @@ and visual language.
Then open:
- Cold Front:
+- Field gear:
- Coatcheck:
- Coatcheck manifest:
- Direct story frame: `http://127.0.0.1:3101/__coatcheck/frame/{story_id}`
@@ -88,8 +96,24 @@ workbench model is documented in [`docs/COATCHECK.md`](docs/COATCHECK.md).
This checkout is linked to its own Clerk development application. Local keys
live only in ignored `.env.local`; never add them to stories, snapshots, or
-source. Login will use official ClerkJS in the browser and verified Clerk
-session JWTs at the Rust boundary.
+source. Official ClerkJS provides optional public-site sign-in. The current
+non-production operator simulator is not authorized by that client-side
+control; server-side Clerk JWT and role verification remains a release gate.
+
+## Commerce staging
+
+Local development uses fictional in-memory inventory unless `DATABASE_URL` is
+set. Railway staging attaches Postgres, runs the embedded migration, and seeds
+the same fictional catalog. Shipping, tax, direct-USDC confirmation, and USPS
+labels are explicit fixtures: no real money or postage is requested.
+
+```sh
+cp .env.example .env.local
+./scripts/dev
+```
+
+Container and Railway configuration live in [`Dockerfile`](Dockerfile) and
+[`railway.toml`](railway.toml).
## Framework note
@@ -108,6 +132,7 @@ workspace toolchain is pinned separately for reproducible builds.
- [Coatcheck browser checks](coatcheck/README.md)
- [Documentation](docs/README.md)
- [Component glossary](docs/COMPONENTS.md)
+ - [Commerce boundary](docs/COMMERCE.md)
- [Coatcheck integration](docs/COATCHECK.md)
- [Product plan](docs/PLAN.md)
- [Workspace scripts](scripts/README.md)
diff --git a/docs/COMMERCE.md b/docs/COMMERCE.md
new file mode 100644
index 0000000..48af655
--- /dev/null
+++ b/docs/COMMERCE.md
@@ -0,0 +1,76 @@
+# Cold Front commerce boundary
+
+Cold Front is the host application for the commerce workflow. The shared
+Topcoat Blocks registry supplies accessible, copy-to-own rendering source;
+Cold Front owns the gritty visual language, catalog data, money math, network
+policy, cart mutations, payment verification, and fulfillment decisions.
+
+## Installed Topcoat blocks
+
+The pinned registry commit is in `Cargo.toml`; the upstream hashes captured at
+installation are recorded in `crates/coldfront-ui/components.toml`. The copied
+source is then formatted and owned by this host.
+
+- product card
+- catalog filter bar
+- cart line item
+- order totals
+- shipping address form
+- payment route selector
+- payment status
+- order receipt
+
+Do not move Cold Front copy, colors, provider names, database calls, or route
+handlers into these block sources. Registry updates are reviewed first in the
+standalone Topcoat Blocks Coatcheck workbench and then copied into this host.
+
+## Staging workflow
+
+1. A public visitor browses fictional field gear and receives an encrypted,
+ opaque cart identifier.
+2. Cart writes require a private-cookie CSRF token.
+3. Checkout accepts and validates a US shipping address, then creates
+ deterministic shipping and tax fixtures.
+4. The visitor chooses a direct-USDC test route: Solana Devnet, Ethereum
+ Sepolia, or Optimism Sepolia.
+5. Reown AppKit may connect a testnet wallet when `REOWN_PROJECT_ID` exists.
+ Wallet connection alone never changes payment or order state.
+6. The staging simulator creates a unique `test_` transaction marker. The
+ server rejects replay and commits the inventory reservation.
+7. The non-production operator console requires a separate, explicit action
+ before it creates a USPS test-environment label fixture.
+
+Postgres stores products, inventory, carts, quotes, orders, reservations,
+payment intents/events, fulfillments, and audit events. Local development may
+use the in-memory fixture backend. Production fails closed without Postgres,
+has no seeded products, rejects checkout, hides the simulator console, and
+requires a persistent cookie key.
+
+## Deliberate release gates
+
+This repository does not yet send USDC, buy postage, calculate production tax,
+or authorize operators server-side. Those are separate integrations with
+their own credentials, webhooks, reconciliation, legal/risk review, and
+browser acceptance evidence.
+
+The public Clerk control remains optional. Do not treat its rendered user
+button as server-side authorization for `/ops`; the operator simulator must
+remain non-production until Clerk JWT verification and an operator role gate
+are implemented.
+
+## Railway staging
+
+Create a Railway service from this repository and attach Postgres. Configure:
+
+```text
+APP_ENV=staging
+DATABASE_URL=
+COOKIE_KEY=
+CLERK_PUBLISHABLE_KEY=
+REOWN_PROJECT_ID=
+```
+
+The Docker image binds `0.0.0.0:$PORT`, runs embedded SQL migrations on boot,
+and exposes `/health`. A deploy is acceptable only after the health endpoint
+passes and the browser workflow reaches catalog, cart, checkout, payment
+simulation, receipt, and label approval.
diff --git a/docs/README.md b/docs/README.md
index 7a87aa0..7cd13a6 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,6 +1,7 @@
# Documentation
- [Component glossary](COMPONENTS.md)
+- [Commerce boundary](COMMERCE.md)
- [Coatcheck integration](COATCHECK.md)
- [Product and platform plan](PLAN.md)
- [Repository root](../README.md)
diff --git a/railway.toml b/railway.toml
new file mode 100644
index 0000000..782144a
--- /dev/null
+++ b/railway.toml
@@ -0,0 +1,9 @@
+[build]
+builder = "DOCKERFILE"
+dockerfilePath = "Dockerfile"
+
+[deploy]
+healthcheckPath = "/health"
+healthcheckTimeout = 120
+restartPolicyType = "ON_FAILURE"
+restartPolicyMaxRetries = 5
From d233f11d149d33c633f346ca7e85453346d39d6f Mon Sep 17 00:00:00 2001
From: Eric Manganaro
Date: Mon, 27 Jul 2026 22:40:25 -0400
Subject: [PATCH 5/5] fix(commerce): fail closed in production
---
apps/coldfront/src/store.rs | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/apps/coldfront/src/store.rs b/apps/coldfront/src/store.rs
index 697439b..ce852ca 100644
--- a/apps/coldfront/src/store.rs
+++ b/apps/coldfront/src/store.rs
@@ -213,6 +213,9 @@ impl CommerceStore {
category: &str,
sort: &str,
) -> Result, StoreError> {
+ if self.environment == "production" {
+ return Ok(Vec::new());
+ }
let mut products = match &self.backend {
Backend::Memory(state) => state.read().await.products.clone(),
Backend::Postgres(pool) => {
@@ -313,6 +316,9 @@ impl CommerceStore {
}
pub async fn add_item(&self, cart_id: Uuid, product_id: Uuid) -> Result<(), StoreError> {
+ if !self.checkout_enabled() {
+ return Err(StoreError::CheckoutDisabled);
+ }
match &self.backend {
Backend::Memory(state) => {
let mut state = state.write().await;
@@ -942,8 +948,11 @@ async fn insert_audit(
#[cfg(test)]
mod tests {
- use super::{CommerceStore, StoreError};
- use crate::domain::{OrderState, ShippingAddress};
+ use std::sync::Arc;
+
+ use super::{Backend, CommerceStore, MemoryState, StoreError};
+ use crate::domain::{OrderState, Product, ShippingAddress};
+ use tokio::sync::RwLock;
use uuid::Uuid;
fn address() -> ShippingAddress {
@@ -1013,4 +1022,28 @@ mod tests {
Err(StoreError::InvalidState)
));
}
+
+ #[tokio::test]
+ async fn production_hides_seeded_rows_and_rejects_cart_writes() {
+ let store = CommerceStore {
+ environment: "production".to_owned(),
+ backend: Backend::Memory(Arc::new(RwLock::new(MemoryState {
+ products: Product::staging_fixtures(),
+ ..MemoryState::default()
+ }))),
+ };
+ let product_id = Product::staging_fixtures()[0].id;
+
+ assert!(
+ store
+ .products("", "", "field-order")
+ .await
+ .expect("catalog lookup")
+ .is_empty()
+ );
+ assert!(matches!(
+ store.add_item(Uuid::new_v4(), product_id).await,
+ Err(StoreError::CheckoutDisabled)
+ ));
+ }
}