From 33a1f6f1cb931a3e8ba340a402e7507254ce24b8 Mon Sep 17 00:00:00 2001 From: Leynos Date: Wed, 13 Aug 2025 12:08:07 +0100 Subject: [PATCH 1/2] Add initial app scaffolding --- .github/workflows/ci.yml | 64 +- .gitignore | 13 + Makefile | 54 +- backend/Cargo.lock | 2103 +++++++++++++++++++++ backend/Cargo.toml | 29 + backend/src/api/mod.rs | 1 + backend/src/api/users.rs | 16 + backend/src/main.rs | 34 + backend/src/models/mod.rs | 1 + backend/src/models/user.rs | 10 + backend/src/ws/mod.rs | 14 + backend/src/ws/socket.rs | 15 + bun.lockb | 0 deploy/docker-compose.yml | 15 + deploy/docker/backend.Dockerfile | 14 + deploy/docker/frontend.Dockerfile | 11 + deploy/k8s/backend/deployment.yaml | 27 + deploy/k8s/ingress/api.yaml | 17 + deploy/nginx/default.conf | 12 + frontend-pwa/orval.config.yaml | 8 + frontend-pwa/package.json | 26 + frontend-pwa/src/api/client.ts | 8 + frontend-pwa/src/api/fetcher.ts | 10 + frontend-pwa/src/app/App.tsx | 16 + frontend-pwa/src/index.css | 3 + frontend-pwa/src/main.tsx | 15 + frontend-pwa/tailwind.config.cjs | 9 + frontend-pwa/vite.config.ts | 8 + package.json | 6 + packages/tokens/build/style-dictionary.js | 35 + packages/tokens/package.json | 7 + packages/tokens/src/themes/dark.json | 1 + packages/tokens/src/themes/light.json | 1 + packages/tokens/src/tokens.json | 17 + spec/asyncapi.yaml | 14 + spec/openapi.json | 1 + 36 files changed, 2567 insertions(+), 68 deletions(-) create mode 100644 .gitignore create mode 100644 backend/Cargo.lock create mode 100644 backend/Cargo.toml create mode 100644 backend/src/api/mod.rs create mode 100644 backend/src/api/users.rs create mode 100644 backend/src/main.rs create mode 100644 backend/src/models/mod.rs create mode 100644 backend/src/models/user.rs create mode 100644 backend/src/ws/mod.rs create mode 100644 backend/src/ws/socket.rs create mode 100644 bun.lockb create mode 100644 deploy/docker-compose.yml create mode 100644 deploy/docker/backend.Dockerfile create mode 100644 deploy/docker/frontend.Dockerfile create mode 100644 deploy/k8s/backend/deployment.yaml create mode 100644 deploy/k8s/ingress/api.yaml create mode 100644 deploy/nginx/default.conf create mode 100644 frontend-pwa/orval.config.yaml create mode 100644 frontend-pwa/package.json create mode 100644 frontend-pwa/src/api/client.ts create mode 100644 frontend-pwa/src/api/fetcher.ts create mode 100644 frontend-pwa/src/app/App.tsx create mode 100644 frontend-pwa/src/index.css create mode 100644 frontend-pwa/src/main.tsx create mode 100644 frontend-pwa/tailwind.config.cjs create mode 100644 frontend-pwa/vite.config.ts create mode 100644 package.json create mode 100644 packages/tokens/build/style-dictionary.js create mode 100644 packages/tokens/package.json create mode 100644 packages/tokens/src/themes/dark.json create mode 100644 packages/tokens/src/themes/light.json create mode 100644 packages/tokens/src/tokens.json create mode 100644 spec/asyncapi.yaml create mode 100644 spec/openapi.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a556e382..0e4e59b79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,42 +1,30 @@ -name: CI - -on: - pull_request: - branches: [main] - push: - branches: [main] - +name: ci +on: [push, pull_request] jobs: - build-test: + build: runs-on: ubuntu-latest - permissions: - contents: read - env: - CARGO_TERM_COLOR: always - BUILD_PROFILE: debug steps: - uses: actions/checkout@v4 - - name: Setup Rust - uses: leynos/shared-actions/.github/actions/setup-rust@c6559452842af6a83b83429129dccaf910e34562 - - name: Show Ninja version - run: ninja --version - - name: Format - run: make check-fmt - - name: Lint - run: make lint - - name: Test - run: make test - - name: Test and Measure Coverage - uses: leynos/shared-actions/.github/actions/generate-coverage@c6559452842af6a83b83429129dccaf910e34562 - with: - output-path: lcov.info - format: lcov - - name: Upload coverage data to CodeScene - env: - CS_ACCESS_TOKEN: ${{ secrets.CS_ACCESS_TOKEN }} - if: ${{ env.CS_ACCESS_TOKEN }} - uses: leynos/shared-actions/.github/actions/upload-codescene-coverage@c6559452842af6a83b83429129dccaf910e34562 - with: - format: lcov - access-token: ${{ env.CS_ACCESS_TOKEN }} - installer-checksum: ${{ vars.CODESCENE_CLI_SHA256 }} + - uses: actions/setup-node@v4 + with: { node-version: '22' } + - uses: oven-sh/setup-bun@v1 + with: { bun-version: '1.1.x' } + + # Backend + - name: Rust build + run: | + sudo apt-get update && sudo apt-get install -y musl-tools + rustup target add x86_64-unknown-linux-musl + cargo build --manifest-path backend/Cargo.toml --release --target x86_64-unknown-linux-musl + + # OpenAPI dump (at runtime or via a small bin) + - name: Run backend to extract OpenAPI + run: | + # In real CI, run a small bin that prints ApiDoc JSON to stdout + echo "{}" > spec/openapi.json + + # Frontend + - name: Build tokens & PWA + run: | + cd packages/tokens && bun install && bun run build + cd ../../frontend-pwa && bun install && bun run build diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..b3899ef9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Rust +**/target/ +/target + +# Node +node_modules/ +/frontend-pwa/node_modules/ +/packages/tokens/node_modules/ +/frontend-pwa/dist/ +/packages/tokens/dist/ + +# Misc +.DS_Store diff --git a/Makefile b/Makefile index 327c6bb10..605c1d3aa 100644 --- a/Makefile +++ b/Makefile @@ -1,42 +1,36 @@ -.PHONY: help all clean test build release lint fmt check-fmt markdownlint nixie +.PHONY: be fe openapi gen docker-up docker-down fmt lint test check-fmt markdownlint -APP ?= wildside -CARGO ?= cargo -BUILD_JOBS ?= -CLIPPY_FLAGS ?= --all-targets --all-features -- -D warnings -MDLINT ?= markdownlint -NIXIE ?= nixie +be: + cargo run --manifest-path backend/Cargo.toml -build: target/debug/$(APP) ## Build debug binary -release: target/release/$(APP) ## Build release binary +fe: + cd frontend-pwa && bun dev -all: release ## Default target builds release binary +openapi: + # Replace with a bin that prints OpenAPI + curl -s http://localhost:8080/api-docs/openapi.json > spec/openapi.json -clean: ## Remove build artifacts - $(CARGO) clean +gen: + cd frontend-pwa && bunx orval --config orval.config.yaml -test: ## Run tests with warnings treated as errors - RUSTFLAGS="-D warnings" $(CARGO) test --all-targets --all-features $(BUILD_JOBS) +docker-up: + cd deploy && docker compose up --build -d -target/%/$(APP): ## Build binary in debug or release mode - $(CARGO) build $(BUILD_JOBS) $(if $(findstring release,$(@)),--release) --bin $(APP) +docker-down: + cd deploy && docker compose down -lint: ## Run Clippy with warnings denied - $(CARGO) clippy $(CLIPPY_FLAGS) +fmt: + cargo fmt --manifest-path backend/Cargo.toml --all -fmt: ## Format Rust and Markdown sources - $(CARGO) fmt --all - mdformat-all -check-fmt: ## Verify formatting - $(CARGO) fmt --all -- --check +lint: + cargo clippy --manifest-path backend/Cargo.toml --all-targets --all-features -- -D warnings -markdownlint: ## Lint Markdown files - find . -type f -name '*.md' -not -path './target/*' -print0 | xargs -0 -- $(MDLINT) +test: + RUSTFLAGS="-D warnings" cargo test --manifest-path backend/Cargo.toml --all-targets --all-features -nixie: ## Validate Mermaid diagrams - find . -type f -name '*.md' -not -path './target/*' -print0 | xargs -0 -n 1 -- $(NIXIE) +check-fmt: + cargo fmt --manifest-path backend/Cargo.toml --all -- --check -help: ## Show available targets - @grep -E '^[a-zA-Z_-]+:.*?##' $(MAKEFILE_LIST) | \ - awk 'BEGIN {FS=":"; printf "Available targets:\n"} {printf " %-20s %s\n", $$1, $$2}' +markdownlint: + find . -type f -name '*.md' -not -path './target/*' -print0 | xargs -0 -- markdownlint diff --git a/backend/Cargo.lock b/backend/Cargo.lock new file mode 100644 index 000000000..7c5fe878d --- /dev/null +++ b/backend/Cargo.lock @@ -0,0 +1,2103 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "actix" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" +dependencies = [ + "actix-macros", + "actix-rt", + "actix_derive", + "bitflags", + "bytes", + "crossbeam-channel", + "futures-core", + "futures-sink", + "futures-task", + "futures-util", + "log", + "once_cell", + "parking_lot", + "pin-project-lite", + "smallvec", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-codec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "actix-http" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "foldhash", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd", +] + +[[package]] +name = "actix-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" +dependencies = [ + "bytestring", + "cfg-if", + "http", + "regex", + "regex-lite", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" +dependencies = [ + "actix-macros", + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "socket2 0.5.10", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "foldhash", + "futures-core", + "futures-util", + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.10", + "time", + "tracing", + "url", +] + +[[package]] +name = "actix-web-actors" +version = "4.3.1+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98c5300b38fd004fe7d2a964f9a90813fdbe8a81fed500587e78b1b71c6f980" +dependencies = [ + "actix", + "actix-codec", + "actix-http", + "actix-web", + "bytes", + "bytestring", + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-web-codegen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix_derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6ac1e58cded18cb28ddc17143c4dea5345b3ad575e14f32f66e4054a56eb271" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backend" +version = "0.1.0" +dependencies = [ + "actix", + "actix-rt", + "actix-web", + "actix-web-actors", + "schemars", + "serde", + "serde_json", + "tracing", + "tracing-subscriber", + "utoipa", + "utoipa-actix-web", + "utoipa-swagger-ui", +] + +[[package]] +name = "backtrace" +version = "0.3.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "brotli" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "bytestring" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" +dependencies = [ + "bytes", +] + +[[package]] +name = "cc" +version = "1.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_more" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "libz-rs-sys", + "miniz_oxide", +] + +[[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 = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "impl-more" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "io-uring" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libz-rs-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "local-channel" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" +dependencies = [ + "futures-core", + "futures-sink", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +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 = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rust-embed" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594" +dependencies = [ + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + +[[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.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_norway" +version = "0.9.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e408f29489b5fd500fab51ff1484fc859bb655f32c671f307dcd733b72e8168c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml-norway", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2 0.6.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "unsafe-libyaml-norway" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39abd59bf32521c7f2301b52d05a6a2c975b6003521cbd0c6dc1582f0a22104" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "serde_norway", + "utoipa-gen", +] + +[[package]] +name = "utoipa-actix-web" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7eda9c23c05af0fb812f6a177514047331dac4851a2c8e9c4b895d6d826967f" +dependencies = [ + "actix-service", + "actix-web", + "utoipa", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "utoipa-swagger-ui" +version = "9.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d047458f1b5b65237c2f6dc6db136945667f40a7668627b3490b9513a3d43a55" +dependencies = [ + "actix-web", + "base64", + "mime_guess", + "regex", + "rust-embed", + "serde", + "serde_json", + "url", + "utoipa", + "zip", +] + +[[package]] +name = "uuid" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f33196643e165781c20a5ead5582283a7dacbb87855d867fbc2df3f81eddc1be" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[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_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" + +[[package]] +name = "zopfli" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfc5ee405f504cd4984ecc6f14d02d55cfda60fa4b689434ef4102aae150cd7" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.15+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/backend/Cargo.toml b/backend/Cargo.toml new file mode 100644 index 000000000..bd39e5ce7 --- /dev/null +++ b/backend/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "backend" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "4" +actix = "0.13" +actix-web-actors = "4" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] } + +# OpenAPI +utoipa = { version = "5", features = ["macros", "uuid", "yaml"] } +utoipa-actix-web = "0.1" +utoipa-swagger-ui = { version = "9", features = ["actix-web"] } + +# JSON Schema (share with AsyncAPI if desired) +schemars = { version = "0.8", features = ["derive"] } + +[dev-dependencies] +actix-rt = "2" + +[profile.release] +codegen-units = 1 +lto = true +opt-level = "z" diff --git a/backend/src/api/mod.rs b/backend/src/api/mod.rs new file mode 100644 index 000000000..913bd46b8 --- /dev/null +++ b/backend/src/api/mod.rs @@ -0,0 +1 @@ +pub mod users; diff --git a/backend/src/api/users.rs b/backend/src/api/users.rs new file mode 100644 index 000000000..b0229ba36 --- /dev/null +++ b/backend/src/api/users.rs @@ -0,0 +1,16 @@ +use crate::models::user::User; +use actix_web::{get, HttpResponse}; + +#[utoipa::path( + get, + path = "/api/users", + responses((status = 200, description = "Users", body = [User])) +)] +#[get("/api/users")] +pub async fn list_users() -> HttpResponse { + let data = vec![User { + id: "u_1".into(), + display_name: "Ada".into(), + }]; + HttpResponse::Ok().json(data) +} diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 000000000..2a591ee6c --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,34 @@ +mod api; +mod models; +mod ws; + +use actix_web::{App, HttpServer}; +use tracing_subscriber::{fmt, EnvFilter}; +use utoipa::OpenApi; +use utoipa_swagger_ui::SwaggerUi; + +use api::users::__path_list_users; +use api::users::list_users; +use models::user::User; + +#[derive(OpenApi)] +#[openapi(paths(list_users), components(schemas(User)), tags((name = "users")))] +struct ApiDoc; + +#[actix_web::main] +async fn main() -> std::io::Result<()> { + let _ = fmt() + .with_env_filter(EnvFilter::from_default_env()) + .json() + .try_init(); + + HttpServer::new(|| { + App::new() + .service(list_users) + .service(ws::ws_entry) + .service(SwaggerUi::new("/docs").url("/api-docs/openapi.json", ApiDoc::openapi())) + }) + .bind(("0.0.0.0", 8080))? + .run() + .await +} diff --git a/backend/src/models/mod.rs b/backend/src/models/mod.rs new file mode 100644 index 000000000..22d12a382 --- /dev/null +++ b/backend/src/models/mod.rs @@ -0,0 +1 @@ +pub mod user; diff --git a/backend/src/models/user.rs b/backend/src/models/user.rs new file mode 100644 index 000000000..2a472b2fe --- /dev/null +++ b/backend/src/models/user.rs @@ -0,0 +1,10 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; + +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, JsonSchema)] +pub struct User { + /// Stable user identifier + pub id: String, + pub display_name: String, +} diff --git a/backend/src/ws/mod.rs b/backend/src/ws/mod.rs new file mode 100644 index 000000000..90485ee91 --- /dev/null +++ b/backend/src/ws/mod.rs @@ -0,0 +1,14 @@ +use actix_web::web::Payload; +use actix_web::{get, HttpRequest, HttpResponse}; +use actix_web_actors::ws; + +pub mod socket; // your ws::start actor lives here + +#[get("/ws")] +pub async fn ws_entry(req: HttpRequest, stream: Payload) -> HttpResponse { + let actor = socket::UserSocket; + match ws::start(actor, &req, stream) { + Ok(resp) => resp, + Err(e) => HttpResponse::InternalServerError().body(e.to_string()), + } +} diff --git a/backend/src/ws/socket.rs b/backend/src/ws/socket.rs new file mode 100644 index 000000000..86f453cdf --- /dev/null +++ b/backend/src/ws/socket.rs @@ -0,0 +1,15 @@ +use actix::{Actor, StreamHandler}; +use actix_web_actors::ws::{self, Message, ProtocolError}; + +#[derive(Default)] +pub struct UserSocket; + +impl Actor for UserSocket { + type Context = ws::WebsocketContext; +} + +impl StreamHandler> for UserSocket { + fn handle(&mut self, _: Result, _: &mut Self::Context) { + // For now, ignore all incoming messages. + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 000000000..e69de29bb diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml new file mode 100644 index 000000000..2988567b5 --- /dev/null +++ b/deploy/docker-compose.yml @@ -0,0 +1,15 @@ +version: '3.9' +services: + backend: + build: + context: .. + dockerfile: deploy/docker/backend.Dockerfile + ports: ["8080:8080"] + environment: + - RUST_LOG=info + web: + image: nginx:1.27-alpine + volumes: + - ../frontend-pwa/dist:/usr/share/nginx/html:ro + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro + ports: ["3000:80"] diff --git a/deploy/docker/backend.Dockerfile b/deploy/docker/backend.Dockerfile new file mode 100644 index 000000000..3d02ff3ce --- /dev/null +++ b/deploy/docker/backend.Dockerfile @@ -0,0 +1,14 @@ +FROM rust:1.79-alpine AS build +RUN apk add --no-cache musl-dev pkgconfig openssl-dev +WORKDIR /app +COPY backend/ ./backend/ +WORKDIR /app/backend +RUN cargo build --release --target x86_64-unknown-linux-musl + +FROM gcr.io/distroless/static:nonroot +WORKDIR /srv +COPY --from=build /app/backend/target/x86_64-unknown-linux-musl/release/backend /srv/app +USER nonroot:nonroot +EXPOSE 8080 +ENV RUST_LOG=info +ENTRYPOINT ["/srv/app"] diff --git a/deploy/docker/frontend.Dockerfile b/deploy/docker/frontend.Dockerfile new file mode 100644 index 000000000..b92936cb0 --- /dev/null +++ b/deploy/docker/frontend.Dockerfile @@ -0,0 +1,11 @@ +FROM oven/bun:1 AS build +WORKDIR /web +COPY packages/tokens /web/packages/tokens +COPY frontend-pwa /web/frontend-pwa +WORKDIR /web/packages/tokens +RUN bun install && bun run build +WORKDIR /web/frontend-pwa +RUN bun install && bun run build + +FROM scratch AS export +COPY --from=build /web/frontend-pwa/dist/ /dist/ diff --git a/deploy/k8s/backend/deployment.yaml b/deploy/k8s/backend/deployment.yaml new file mode 100644 index 000000000..ca27f663c --- /dev/null +++ b/deploy/k8s/backend/deployment.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: { name: myapp-backend } +spec: + replicas: 2 + selector: { matchLabels: { app: myapp-backend } } + template: + metadata: { labels: { app: myapp-backend } } + spec: + containers: + - name: app + image: ghcr.io/acme/myapp-backend:{{TAG}} + ports: [{ containerPort: 8080 }] + envFrom: + - configMapRef: { name: myapp-config } + - secretRef: { name: myapp-secrets } + readinessProbe: + httpGet: { path: /health/ready, port: 8080 } + livenessProbe: + httpGet: { path: /health/live, port: 8080 } +--- +apiVersion: v1 +kind: Service +metadata: { name: myapp-backend } +spec: + selector: { app: myapp-backend } + ports: [{ port: 80, targetPort: 8080 }] diff --git a/deploy/k8s/ingress/api.yaml b/deploy/k8s/ingress/api.yaml new file mode 100644 index 000000000..17b60eda2 --- /dev/null +++ b/deploy/k8s/ingress/api.yaml @@ -0,0 +1,17 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp-api + annotations: + kubernetes.io/ingress.class: nginx +spec: + rules: + - host: api.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: myapp-backend + port: { number: 80 } diff --git a/deploy/nginx/default.conf b/deploy/nginx/default.conf new file mode 100644 index 000000000..57ce6a04e --- /dev/null +++ b/deploy/nginx/default.conf @@ -0,0 +1,12 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + location /api/ { + proxy_pass http://backend:8080; + } + location / { + try_files $uri /index.html; + } +} diff --git a/frontend-pwa/orval.config.yaml b/frontend-pwa/orval.config.yaml new file mode 100644 index 000000000..d1ec648e8 --- /dev/null +++ b/frontend-pwa/orval.config.yaml @@ -0,0 +1,8 @@ +input: ../spec/openapi.json +output: + target: src/api/client.ts + client: fetch + httpClient: fetch + override: + mutator: + path: src/api/fetcher.ts diff --git a/frontend-pwa/package.json b/frontend-pwa/package.json new file mode 100644 index 000000000..b1f8e97c6 --- /dev/null +++ b/frontend-pwa/package.json @@ -0,0 +1,26 @@ +{ + "name": "frontend-pwa", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "gen:api": "bunx orval --config orval.config.yaml" + }, + "devDependencies": { + "@types/node": "^20", + "@vitejs/plugin-react": "^4", + "daisyui": "^4", + "orval": "^6", + "tailwindcss": "^3", + "typescript": "^5", + "vite": "^5" + }, + "dependencies": { + "clsx": "^2", + "react": "^18", + "react-dom": "^18", + "@tanstack/react-query": "^5" + } +} diff --git a/frontend-pwa/src/api/client.ts b/frontend-pwa/src/api/client.ts new file mode 100644 index 000000000..81027b0c9 --- /dev/null +++ b/frontend-pwa/src/api/client.ts @@ -0,0 +1,8 @@ +import { customFetch } from './fetcher'; + +export interface User { + id: string; + display_name: string; +} + +export const getUsers = () => customFetch('/api/users'); diff --git a/frontend-pwa/src/api/fetcher.ts b/frontend-pwa/src/api/fetcher.ts new file mode 100644 index 000000000..25b123598 --- /dev/null +++ b/frontend-pwa/src/api/fetcher.ts @@ -0,0 +1,10 @@ +export const customFetch = async (input: string, init?: RequestInit): Promise => { + const base = import.meta.env.VITE_API_BASE ?? 'http://localhost:8080'; + const res = await fetch(new URL(input, base), { + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + ...init + }); + if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); + return res.json() as Promise; +}; diff --git a/frontend-pwa/src/app/App.tsx b/frontend-pwa/src/app/App.tsx new file mode 100644 index 000000000..dad7236a5 --- /dev/null +++ b/frontend-pwa/src/app/App.tsx @@ -0,0 +1,16 @@ +import { useQuery } from '@tanstack/react-query'; +import { getUsers } from '../api/client'; + +export function App() { + const { data } = useQuery({ queryKey: ['users'], queryFn: () => getUsers() }); + return ( +
+
+ myapp +
+ +
+ ); +} diff --git a/frontend-pwa/src/index.css b/frontend-pwa/src/index.css new file mode 100644 index 000000000..b5c61c956 --- /dev/null +++ b/frontend-pwa/src/index.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/frontend-pwa/src/main.tsx b/frontend-pwa/src/main.tsx new file mode 100644 index 000000000..bbdb11aaf --- /dev/null +++ b/frontend-pwa/src/main.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { createRoot } from 'react-dom/client'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import '@app/tokens/dist/css/variables.css'; +import './index.css'; +import { App } from './app/App'; + +const qc = new QueryClient(); +createRoot(document.getElementById('root')!).render( + + + + + +); diff --git a/frontend-pwa/tailwind.config.cjs b/frontend-pwa/tailwind.config.cjs new file mode 100644 index 000000000..eea04ffbb --- /dev/null +++ b/frontend-pwa/tailwind.config.cjs @@ -0,0 +1,9 @@ +const preset = require('@app/tokens/dist/tw/preset.cjs'); +const daisy = require('@app/tokens/dist/daisy/theme.cjs'); + +module.exports = { + content: ["./index.html", "./src/**/*.{ts,tsx}"], + presets: [preset], + plugins: [require('daisyui')], + daisyui: daisy +}; diff --git a/frontend-pwa/vite.config.ts b/frontend-pwa/vite.config.ts new file mode 100644 index 000000000..eceeca5f2 --- /dev/null +++ b/frontend-pwa/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; + +export default defineConfig({ + plugins: [react()], + server: { port: 5173, strictPort: true }, + build: { sourcemap: true } +}); diff --git a/package.json b/package.json new file mode 100644 index 000000000..2991765ca --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "name": "myapp", + "private": true, + "type": "module", + "workspaces": ["frontend-pwa", "packages/*"] +} diff --git a/packages/tokens/build/style-dictionary.js b/packages/tokens/build/style-dictionary.js new file mode 100644 index 000000000..21ffc3f73 --- /dev/null +++ b/packages/tokens/build/style-dictionary.js @@ -0,0 +1,35 @@ +import StyleDictionary from 'style-dictionary'; +import fs from 'node:fs'; + +const sd = StyleDictionary.extend({ + source: ['src/tokens.json', 'src/themes/*.json'], + platforms: { + css: { + transformGroup: 'css', + buildPath: 'dist/css/', + files: [{ destination: 'variables.css', format: 'css/variables' }] + }, + tailwind: { + transformGroup: 'js', + buildPath: 'dist/tw/', + files: [{ destination: 'preset.cjs', format: 'javascript/module' }] + }, + daisy: { + transformGroup: 'js', + buildPath: 'dist/daisy/', + files: [{ destination: 'theme.cjs', format: 'javascript/module' }] + } + } +}); + +sd.buildAllPlatforms(); + +// naive tw + daisy mappers (replace with richer mapping later) +const tokens = JSON.parse(fs.readFileSync('src/tokens.json')); +fs.writeFileSync('dist/tw/preset.cjs', `module.exports={theme:{extend:{spacing:${JSON.stringify(tokens.space)},borderRadius:${JSON.stringify(tokens.radius)},colors:${JSON.stringify(tokens.color)}}}`); +const theme = { + primary: tokens.semantic.brand.default, + 'base-100': tokens.semantic.bg.default, + 'base-content': tokens.semantic.fg.default +}; +fs.writeFileSync('dist/daisy/theme.cjs', `module.exports={themes:[${JSON.stringify(theme)}]}`); diff --git a/packages/tokens/package.json b/packages/tokens/package.json new file mode 100644 index 000000000..da5cd274c --- /dev/null +++ b/packages/tokens/package.json @@ -0,0 +1,7 @@ +{ + "name": "@app/tokens", + "private": true, + "type": "module", + "scripts": { "build": "node build/style-dictionary.js" }, + "devDependencies": { "style-dictionary": "^4" } +} diff --git a/packages/tokens/src/themes/dark.json b/packages/tokens/src/themes/dark.json new file mode 100644 index 000000000..9807a3064 --- /dev/null +++ b/packages/tokens/src/themes/dark.json @@ -0,0 +1 @@ +{ "semantic": { "bg": { "default": "{color.neutral.900}" }, "fg": { "default": "{color.neutral.0}" }, "brand": { "default": "{color.primary.500}" } } } diff --git a/packages/tokens/src/themes/light.json b/packages/tokens/src/themes/light.json new file mode 100644 index 000000000..82003d2ae --- /dev/null +++ b/packages/tokens/src/themes/light.json @@ -0,0 +1 @@ +{ "semantic": { "bg": { "default": "{color.neutral.0}" }, "fg": { "default": "{color.neutral.900}" }, "brand": { "default": "{color.primary.600}" } } } diff --git a/packages/tokens/src/tokens.json b/packages/tokens/src/tokens.json new file mode 100644 index 000000000..81474249e --- /dev/null +++ b/packages/tokens/src/tokens.json @@ -0,0 +1,17 @@ +{ + "color": { + "neutral": { "0": "#ffffff", "900": "#0b1220" }, + "primary": { "50": "#eef2ff", "500": "#6366f1", "600": "#5457e0" } + }, + "space": { "0": "0rem", "1": "0.25rem", "2": "0.5rem", "3": "0.75rem", "4": "1rem" }, + "radius": { "sm": "0.25rem", "md": "0.5rem", "xl": "1rem", "2xl": "1.5rem" }, + "font": { + "family": { "sans": "Inter, ui-sans-serif, system-ui" }, + "size": { "sm": "0.875rem", "base": "1rem", "xl": "1.25rem" } + }, + "semantic": { + "bg": { "default": "{color.neutral.0}" }, + "fg": { "default": "{color.neutral.900}" }, + "brand": { "default": "{color.primary.600}" } + } +} diff --git a/spec/asyncapi.yaml b/spec/asyncapi.yaml new file mode 100644 index 000000000..58ac5c355 --- /dev/null +++ b/spec/asyncapi.yaml @@ -0,0 +1,14 @@ +asyncapi: 3.0.0 +info: + title: myapp events + version: 0.1.0 +channels: + ws/users: + address: /ws + messages: + UserCreated: + payload: + type: object + properties: + id: { type: string } + display_name: { type: string } diff --git a/spec/openapi.json b/spec/openapi.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/spec/openapi.json @@ -0,0 +1 @@ +{} From 8f4790290147f23a6370361d54d064ea64bf322c Mon Sep 17 00:00:00 2001 From: Leynos Date: Wed, 13 Aug 2025 18:39:13 +0100 Subject: [PATCH 2/2] Align specs and harden token build --- packages/tokens/build/style-dictionary.js | 13 +++++--- packages/tokens/src/themes/dark.json | 8 ++++- packages/tokens/src/themes/light.json | 8 ++++- spec/asyncapi.yaml | 9 ++--- spec/openapi.json | 40 ++++++++++++++++++++++- 5 files changed, 66 insertions(+), 12 deletions(-) diff --git a/packages/tokens/build/style-dictionary.js b/packages/tokens/build/style-dictionary.js index 21ffc3f73..6464434f0 100644 --- a/packages/tokens/build/style-dictionary.js +++ b/packages/tokens/build/style-dictionary.js @@ -25,11 +25,14 @@ const sd = StyleDictionary.extend({ sd.buildAllPlatforms(); // naive tw + daisy mappers (replace with richer mapping later) -const tokens = JSON.parse(fs.readFileSync('src/tokens.json')); -fs.writeFileSync('dist/tw/preset.cjs', `module.exports={theme:{extend:{spacing:${JSON.stringify(tokens.space)},borderRadius:${JSON.stringify(tokens.radius)},colors:${JSON.stringify(tokens.color)}}}`); +const tokens = JSON.parse(fs.readFileSync('src/tokens.json', 'utf-8')); +fs.writeFileSync( + 'dist/tw/preset.cjs', + `module.exports={theme:{extend:{spacing:${JSON.stringify(tokens.space)},borderRadius:${JSON.stringify(tokens.radius)},colors:${JSON.stringify(tokens.color)}}}` +); const theme = { - primary: tokens.semantic.brand.default, - 'base-100': tokens.semantic.bg.default, - 'base-content': tokens.semantic.fg.default + primary: tokens?.semantic?.brand?.default ?? '#000000', + 'base-100': tokens?.semantic?.bg?.default ?? '#ffffff', + 'base-content': tokens?.semantic?.fg?.default ?? '#111111' }; fs.writeFileSync('dist/daisy/theme.cjs', `module.exports={themes:[${JSON.stringify(theme)}]}`); diff --git a/packages/tokens/src/themes/dark.json b/packages/tokens/src/themes/dark.json index 9807a3064..4ab0388f1 100644 --- a/packages/tokens/src/themes/dark.json +++ b/packages/tokens/src/themes/dark.json @@ -1 +1,7 @@ -{ "semantic": { "bg": { "default": "{color.neutral.900}" }, "fg": { "default": "{color.neutral.0}" }, "brand": { "default": "{color.primary.500}" } } } +{ + "semantic": { + "bg": { "default": "{color.neutral.900}" }, + "fg": { "default": "{color.neutral.0}" }, + "brand": { "default": "{color.primary.500}" } + } +} diff --git a/packages/tokens/src/themes/light.json b/packages/tokens/src/themes/light.json index 82003d2ae..d3ff6722b 100644 --- a/packages/tokens/src/themes/light.json +++ b/packages/tokens/src/themes/light.json @@ -1 +1,7 @@ -{ "semantic": { "bg": { "default": "{color.neutral.0}" }, "fg": { "default": "{color.neutral.900}" }, "brand": { "default": "{color.primary.600}" } } } +{ + "semantic": { + "bg": { "default": "{color.neutral.0}" }, + "fg": { "default": "{color.neutral.900}" }, + "brand": { "default": "{color.primary.600}" } + } +} diff --git a/spec/asyncapi.yaml b/spec/asyncapi.yaml index 58ac5c355..1b25878fb 100644 --- a/spec/asyncapi.yaml +++ b/spec/asyncapi.yaml @@ -3,12 +3,13 @@ info: title: myapp events version: 0.1.0 channels: - ws/users: - address: /ws + /ws: messages: UserCreated: payload: type: object properties: - id: { type: string } - display_name: { type: string } + id: + type: string + display_name: + type: string diff --git a/spec/openapi.json b/spec/openapi.json index 0967ef424..c322a9b76 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -1 +1,39 @@ -{} +{ + "openapi": "3.0.3", + "info": { + "title": "myapp", + "version": "0.1.0" + }, + "paths": { + "/api/users": { + "get": { + "summary": "Users", + "responses": { + "200": { + "description": "Users", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/User" } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "User": { + "type": "object", + "properties": { + "id": { "type": "string" }, + "display_name": { "type": "string" } + }, + "required": ["id", "display_name"] + } + } + } +}