refactor(quotesdb): collapse to single crate with api and ui binaries
Replaces the three separate sub-crates (api/, ui/, tests/) with a single Cargo crate at the quotesdb/ root. Shared code lives in src/lib.rs; the api and ui are multi-binary targets; integration tests use the standard Cargo tests/ layout. Trunk files moved to project root with data-bin="ui". Closes ticket b38032. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main
parent
775761929c
commit
bd0c2af085
@ -0,0 +1,233 @@
|
||||
+++
|
||||
title = "Refactor to single-crate with api and ui binaries"
|
||||
priority = 8
|
||||
status = "in_progress"
|
||||
ticket_type = "task"
|
||||
dependencies = ["ec118c"]
|
||||
+++
|
||||
|
||||
## Goal
|
||||
|
||||
Collapse the three separate sub-crates (`api/`, `ui/`, `tests/`) into a single Cargo crate rooted at `quotesdb/`. This simplifies the project structure, enables direct code sharing between the api and ui via `src/lib.rs`, and makes `cargo test` run all tests (unit + integration) in a single invocation.
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
```
|
||||
quotesdb/
|
||||
├── api/ # independent crate "quotesdb-api"
|
||||
│ ├── Cargo.toml
|
||||
│ ├── src/main.rs
|
||||
│ ├── src/tests.rs
|
||||
│ ├── docs/
|
||||
│ └── README.md
|
||||
├── ui/ # independent crate "quotesdb-ui"
|
||||
│ ├── Cargo.toml
|
||||
│ ├── src/main.rs
|
||||
│ ├── src/tests.rs
|
||||
│ ├── index.html
|
||||
│ ├── Trunk.toml
|
||||
│ ├── docs/
|
||||
│ └── README.md
|
||||
├── tests/ # independent crate "quotesdb-tests"
|
||||
│ ├── Cargo.toml
|
||||
│ ├── docs/
|
||||
│ └── README.md
|
||||
├── infra/
|
||||
└── docs/
|
||||
```
|
||||
|
||||
**Problems with current structure:**
|
||||
- Shared types/logic must go through `../../common` — no quotesdb-specific shared code.
|
||||
- Running tests requires `cd`ing into each sub-crate separately.
|
||||
- Three `Cargo.toml` files to maintain, three `cargo fmt/check/clippy` invocations.
|
||||
- Trunk must be run from `ui/`, not the project root.
|
||||
|
||||
---
|
||||
|
||||
## Target State
|
||||
|
||||
```
|
||||
quotesdb/
|
||||
├── Cargo.toml # single crate "quotesdb", default-run = "api"
|
||||
├── src/
|
||||
│ ├── lib.rs # shared code (types, models, auth logic, etc.)
|
||||
│ └── bin/
|
||||
│ ├── api/
|
||||
│ │ └── main.rs # api binary entrypoint
|
||||
│ └── ui/
|
||||
│ └── main.rs # ui binary entrypoint (for Trunk)
|
||||
├── tests/ # integration tests — run by `cargo test`
|
||||
│ └── (*.rs files)
|
||||
├── index.html # Trunk HTML entry (moved from ui/)
|
||||
├── Trunk.toml # updated to point to ui binary
|
||||
├── infra/
|
||||
└── docs/
|
||||
├── PLANNING.md
|
||||
├── ARCHITECTURE.md
|
||||
└── plans/
|
||||
└── 2026-02-27-quotesdb-design.md
|
||||
```
|
||||
|
||||
**Developer workflow after refactor (unchanged from user perspective):**
|
||||
- `cargo run` — starts the API server (default binary is `api`)
|
||||
- `trunk serve` — compiles ui to Wasm and serves it
|
||||
- `cargo test` — runs unit tests + integration tests
|
||||
|
||||
---
|
||||
|
||||
## Changes Required
|
||||
|
||||
### 1. Create `quotesdb/Cargo.toml`
|
||||
|
||||
Single crate manifest with:
|
||||
- `name = "quotesdb"`
|
||||
- `default-run = "api"` — ensures `cargo run` launches the api
|
||||
- `edition = "2021"`, `license = "MIT OR Apache-2.0"`
|
||||
- `[profile.release]` block (opt-level z, lto, strip, codegen-units 1)
|
||||
- All api dependencies (axum, tokio, workers-rs, sqlx, nanoid, etc.)
|
||||
- All ui dependencies (yew, wasm-bindgen, web-sys, etc.)
|
||||
- All test dependencies (reqwest, tokio, etc.) under `[dev-dependencies]`
|
||||
- Two `[[bin]]` entries:
|
||||
```toml
|
||||
[[bin]]
|
||||
name = "api"
|
||||
path = "src/bin/api/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ui"
|
||||
path = "src/bin/ui/main.rs"
|
||||
```
|
||||
|
||||
### 2. Create `src/lib.rs`
|
||||
|
||||
Shared module for code used by both binaries:
|
||||
- Domain types: `Quote`, `QuoteTag`, pagination structs, request/response shapes
|
||||
- Auth code generation (4-word passphrase) — shared so ui can display it and api generates it
|
||||
- NanoID generation utility
|
||||
- Any other shared logic
|
||||
|
||||
### 3. Move api source
|
||||
|
||||
- `api/src/main.rs` → `src/bin/api/main.rs`
|
||||
- `api/src/tests.rs` → `src/bin/api/tests.rs` (or inline unit tests within the binary module)
|
||||
- Delete `api/Cargo.toml`
|
||||
|
||||
### 4. Move ui source
|
||||
|
||||
- `ui/src/main.rs` → `src/bin/ui/main.rs`
|
||||
- `ui/src/tests.rs` → `src/bin/ui/tests.rs`
|
||||
- Delete `ui/Cargo.toml`
|
||||
|
||||
### 5. Move Trunk files
|
||||
|
||||
- `ui/index.html` → `quotesdb/index.html`
|
||||
- `ui/Trunk.toml` → `quotesdb/Trunk.toml`
|
||||
|
||||
Update `Trunk.toml` to explicitly name the ui binary so Trunk compiles the right entrypoint:
|
||||
```toml
|
||||
[build]
|
||||
target = "index.html"
|
||||
|
||||
[build.cargo]
|
||||
args = ["--bin", "ui"]
|
||||
```
|
||||
|
||||
### 6. Move integration tests
|
||||
|
||||
- Content from `tests/` sub-crate moves into `quotesdb/tests/` as `.rs` files (standard Cargo integration test layout).
|
||||
- Delete `tests/Cargo.toml`.
|
||||
- Integration tests import from the crate root (`use quotesdb::...`) and from dev-dependencies.
|
||||
- They run with `cargo test` automatically — no separate crate needed.
|
||||
|
||||
### 7. Consolidate docs
|
||||
|
||||
Merge per-sub-crate docs into the project-level `docs/` directory:
|
||||
- `api/docs/PLANNING.md` and `ui/docs/PLANNING.md` → merge into `docs/PLANNING.md`
|
||||
- `api/docs/ARCHITECTURE.md` and `ui/docs/ARCHITECTURE.md` → merge into `docs/ARCHITECTURE.md`
|
||||
- `api/README.md` and `ui/README.md` and `tests/README.md` → consolidate into the project-level `quotesdb/README.md`
|
||||
- Delete the now-empty `api/docs/`, `ui/docs/`, `tests/docs/` directories.
|
||||
|
||||
### 8. Update `CLAUDE.md`
|
||||
|
||||
Update `quotesdb/CLAUDE.md` to reflect:
|
||||
- New directory structure (single crate, not three sub-crates)
|
||||
- New validation commands run from `quotesdb/` root, not from sub-directories
|
||||
- Updated branch naming and ticket hierarchy (the sub-project split is now logical, not a file-system split)
|
||||
- Updated agent dispatch instructions (agents work in `src/bin/api/` or `src/bin/ui/`, not separate crates)
|
||||
|
||||
### 9. Delete orphaned sub-crate roots
|
||||
|
||||
After moving all contents:
|
||||
- Delete `api/` directory entirely
|
||||
- Delete `ui/` directory entirely
|
||||
- Delete `tests/` old sub-crate directory (but `quotesdb/tests/` integration test files stay)
|
||||
|
||||
---
|
||||
|
||||
## Considerations & Complications
|
||||
|
||||
### Compilation targets
|
||||
|
||||
The `api` binary compiles for the **host** target during local dev (`cargo run`). The `ui` binary compiles for `wasm32-unknown-unknown` via Trunk. These are separate compilation invocations — they don't conflict in a single Cargo crate.
|
||||
|
||||
However, **shared code in `src/lib.rs` must compile for both targets.** Avoid host-only APIs (threading, filesystem) in `lib.rs`. Use `#[cfg(target_arch = "wasm32")]` and `#[cfg(not(target_arch = "wasm32"))]` guards where needed.
|
||||
|
||||
### `cargo test` and Wasm
|
||||
|
||||
`cargo test` runs on the host target. The `ui` binary's tests cannot use DOM/browser APIs directly. Yew component tests that require a browser context must use `wasm-bindgen-test` and `wasm-pack test` — these cannot be run by `cargo test`. Therefore:
|
||||
- Unit tests in `src/bin/ui/` must be limited to pure logic (routing, data transformations, API client request construction) guarded with `#[cfg(test)]`.
|
||||
- Browser-only tests (component rendering) are out of scope for `cargo test` and remain a future concern.
|
||||
- Integration tests in `tests/` exercise the **api** only and run on the host — these work fine with `cargo test`.
|
||||
|
||||
### workers-rs and local dev
|
||||
|
||||
The api uses `workers-rs` for Cloudflare Workers deployment. For local development `cargo run`, the api should either:
|
||||
- Use a plain Axum server (conditional compilation: `#[cfg(not(target_env = "worker"))]`), OR
|
||||
- Use the workers-rs local dev entrypoint.
|
||||
|
||||
The existing `api/src/main.rs` stub is empty — the implementation tickets will determine the approach. This ticket should preserve the stub structure and make no assumptions about the final api implementation.
|
||||
|
||||
### Dependency conflicts
|
||||
|
||||
Some dependencies may not compile for all targets. Use `[target.'cfg(not(target_arch = "wasm32"))'.dependencies]` for api-only deps and `[target.'cfg(target_arch = "wasm32")'.dependencies]` for ui-only deps in `Cargo.toml` where needed. This keeps compile times reasonable and avoids linker conflicts.
|
||||
|
||||
---
|
||||
|
||||
## Validation
|
||||
|
||||
From `quotesdb/` root:
|
||||
```sh
|
||||
cargo fmt # must pass cleanly
|
||||
cargo check # must pass for host target
|
||||
cargo clippy # must pass with no warnings
|
||||
cargo test # must run and pass all tests (unit + integration)
|
||||
trunk build # must successfully compile the ui binary to wasm
|
||||
```
|
||||
|
||||
The conventional commit for this work: `refactor(quotesdb): collapse to single crate with api and ui binaries`
|
||||
|
||||
---
|
||||
|
||||
## Files to Create / Move / Delete (Summary)
|
||||
|
||||
| Action | Path |
|
||||
|--------|------|
|
||||
| CREATE | `quotesdb/Cargo.toml` |
|
||||
| CREATE | `quotesdb/src/lib.rs` |
|
||||
| MOVE | `api/src/main.rs` → `src/bin/api/main.rs` |
|
||||
| MOVE | `api/src/tests.rs` → `src/bin/api/tests.rs` |
|
||||
| MOVE | `ui/src/main.rs` → `src/bin/ui/main.rs` |
|
||||
| MOVE | `ui/src/tests.rs` → `src/bin/ui/tests.rs` |
|
||||
| MOVE | `ui/index.html` → `index.html` |
|
||||
| MOVE | `ui/Trunk.toml` → `Trunk.toml` (update `--bin ui`) |
|
||||
| MERGE | `api/docs/` + `ui/docs/` + `tests/docs/` → `docs/` |
|
||||
| MERGE | `api/README.md`, `ui/README.md`, `tests/README.md` → `README.md` |
|
||||
| DELETE | `api/Cargo.toml` |
|
||||
| DELETE | `ui/Cargo.toml` |
|
||||
| DELETE | `tests/Cargo.toml` |
|
||||
| DELETE | `api/` (after moving contents) |
|
||||
| DELETE | `ui/` (after moving contents) |
|
||||
| UPDATE | `Trunk.toml` (add `[build.cargo] args = ["--bin", "ui"]`) |
|
||||
| UPDATE | `quotesdb/CLAUDE.md` (structure, validation paths, agent instructions) |
|
||||
@ -0,0 +1,14 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "quotesdb"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"common",
|
||||
]
|
||||
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "quotesdb"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
default-run = "api"
|
||||
|
||||
[[bin]]
|
||||
name = "api"
|
||||
path = "src/bin/api/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ui"
|
||||
path = "src/bin/ui/main.rs"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
@ -0,0 +1,40 @@
|
||||
# quotesdb
|
||||
|
||||
A quotes web application — browse, submit, and manage memorable quotes.
|
||||
|
||||
## What
|
||||
|
||||
quotesdb is a full-stack web application with:
|
||||
- A JSON REST API (`api` binary) backed by Cloudflare Workers + D1 (SQLite)
|
||||
- A Yew/Wasm frontend (`ui` binary) hosted on Cloudflare Pages
|
||||
- NanoID-identified quotes protected by a 4-word passphrase auth code
|
||||
|
||||
## How
|
||||
|
||||
Single Cargo crate with two binaries sharing common types via `src/lib.rs`:
|
||||
- `api`: Axum on Tokio, targeting Cloudflare Workers via workers-rs, SQLx + D1
|
||||
- `ui`: Yew compiled to `wasm32-unknown-unknown` via Trunk
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
# Start API server (local dev)
|
||||
cargo run
|
||||
|
||||
# Start UI dev server (requires wasm32 toolchain + trunk)
|
||||
trunk serve
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```sh
|
||||
cargo fmt && cargo check && cargo clippy && cargo test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of [Apache License, Version 2.0](../LICENSE-APACHE) or [MIT License](../LICENSE-MIT) at your option.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software was written with [Claude Code](https://claude.ai/claude-code) (claude-sonnet-4-6).
|
||||
@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "quotesdb-api"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../../common" }
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
@ -1,31 +0,0 @@
|
||||
# quotesdb-api
|
||||
|
||||
Rust/Axum backend for the quotesdb quotes service, deployed as a Cloudflare Worker.
|
||||
|
||||
## What
|
||||
|
||||
A JSON REST API for creating, browsing, and managing quotes. Each quote is identified by a NanoID and protected by a 4-word passphrase auth code.
|
||||
|
||||
## How
|
||||
|
||||
Built with Axum on Tokio, targeting Cloudflare Workers via `workers-rs`. Data is stored in Cloudflare D1 (SQLite-compatible) in production and Turso file-backed SQLite locally. Queries use SQLx.
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
cargo run
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```sh
|
||||
cargo fmt && cargo check && cargo clippy && cargo test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of [Apache License, Version 2.0](../../LICENSE-APACHE) or [MIT License](../../LICENSE-MIT) at your option.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software was written with [Claude Code](https://claude.ai/claude-code) (claude-sonnet-4-6).
|
||||
@ -1,9 +0,0 @@
|
||||
# quotesdb-api — Architecture
|
||||
|
||||
## Component Overview
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
|
||||
## Component Interactions
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
@ -1,9 +0,0 @@
|
||||
# quotesdb-api — Planning
|
||||
|
||||
## Development Phases
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
|
||||
## Work Log
|
||||
|
||||
_Updated as work progresses._
|
||||
@ -1 +0,0 @@
|
||||
fn main() {}
|
||||
@ -1,2 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
@ -0,0 +1,468 @@
|
||||
# Single-Crate Refactor Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Collapse `api/`, `ui/`, and `tests/` sub-crates into a single `quotesdb` Cargo crate with two binaries (`api`, `ui`) and standard integration tests.
|
||||
|
||||
**Architecture:** One `Cargo.toml` at `quotesdb/` root; api code lives in `src/bin/api/main.rs`; ui code in `src/bin/ui/main.rs`; shared types in `src/lib.rs`; integration tests in `tests/` as plain `.rs` files. All source files are currently stubs so migration is purely structural.
|
||||
|
||||
**Tech Stack:** Rust, Cargo multi-binary layout, Trunk (for Wasm/ui), nbd (ticket tracking)
|
||||
|
||||
---
|
||||
|
||||
## Pre-flight
|
||||
|
||||
All source files are stubs (`fn main() {}`). No logic to preserve. Validation commands run from `quotesdb/` root:
|
||||
|
||||
```sh
|
||||
cargo fmt && cargo check && cargo clippy && cargo test
|
||||
```
|
||||
|
||||
Trunk validation (requires wasm32 toolchain):
|
||||
```sh
|
||||
trunk build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Mark ticket in progress
|
||||
|
||||
**Step 1: Update ticket status**
|
||||
|
||||
```sh
|
||||
nbd update b38032 --status in_progress --json
|
||||
```
|
||||
|
||||
Expected: JSON with `"status": "in_progress"`.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Create directory structure
|
||||
|
||||
**Step 1: Create binary directories**
|
||||
|
||||
```sh
|
||||
mkdir -p src/bin/api src/bin/ui
|
||||
```
|
||||
|
||||
No output expected.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Create root `Cargo.toml`
|
||||
|
||||
**Files:**
|
||||
- Create: `Cargo.toml`
|
||||
|
||||
**Step 1: Write `Cargo.toml`**
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "quotesdb"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
default-run = "api"
|
||||
|
||||
[[bin]]
|
||||
name = "api"
|
||||
path = "src/bin/api/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "ui"
|
||||
path = "src/bin/ui/main.rs"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../common" }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
```
|
||||
|
||||
Note: Dependencies stay minimal — just what the stubs need (nothing). Additional deps are added in implementation tickets.
|
||||
|
||||
**Step 2: Verify it's parseable**
|
||||
|
||||
```sh
|
||||
cargo metadata --no-deps --manifest-path Cargo.toml
|
||||
```
|
||||
|
||||
Expected: JSON output without errors.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Create `src/lib.rs`
|
||||
|
||||
**Files:**
|
||||
- Create: `src/lib.rs`
|
||||
|
||||
**Step 1: Write `src/lib.rs`**
|
||||
|
||||
```rust
|
||||
//! Shared types and utilities for the `quotesdb` crate.
|
||||
//!
|
||||
//! This module is used by both the `api` and `ui` binaries.
|
||||
//! Code placed here must compile for both the host target (api)
|
||||
//! and `wasm32-unknown-unknown` (ui).
|
||||
//!
|
||||
//! Use `#[cfg(not(target_arch = "wasm32"))]` for host-only items
|
||||
//! and `#[cfg(target_arch = "wasm32")]` for wasm-only items.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Create `src/bin/api/main.rs`
|
||||
|
||||
**Files:**
|
||||
- Create: `src/bin/api/main.rs`
|
||||
|
||||
**Step 1: Write the api binary entrypoint**
|
||||
|
||||
```rust
|
||||
//! API server binary entrypoint.
|
||||
//!
|
||||
//! Runs the quotesdb REST API. In production this targets Cloudflare Workers
|
||||
//! via workers-rs. For local development it runs a plain Axum/Tokio server.
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
```
|
||||
|
||||
Note: The old `api/src/tests.rs` was a separate file imported as a module. Here we inline the empty test module directly — simpler for a stub.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Create `src/bin/ui/main.rs`
|
||||
|
||||
**Files:**
|
||||
- Create: `src/bin/ui/main.rs`
|
||||
|
||||
**Step 1: Write the ui binary entrypoint**
|
||||
|
||||
```rust
|
||||
//! UI binary entrypoint.
|
||||
//!
|
||||
//! Compiled to WebAssembly via Trunk targeting `wasm32-unknown-unknown`.
|
||||
//! Runs the Yew frontend application.
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Verify `cargo check` passes
|
||||
|
||||
**Step 1: Run check**
|
||||
|
||||
```sh
|
||||
cargo check
|
||||
```
|
||||
|
||||
Expected: No errors. If the `common` path dependency causes issues, verify `../common` is correct relative to `quotesdb/`.
|
||||
|
||||
**Step 2: Run full validation**
|
||||
|
||||
```sh
|
||||
cargo fmt && cargo check && cargo clippy && cargo test
|
||||
```
|
||||
|
||||
Expected: All pass. `cargo test` will report 0 tests but exit 0.
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Move Trunk files
|
||||
|
||||
**Files:**
|
||||
- Create: `index.html` (moved from `ui/index.html`)
|
||||
- Create: `Trunk.toml` (moved from `ui/Trunk.toml`, updated)
|
||||
|
||||
**Step 1: Write `index.html`**
|
||||
|
||||
Content is identical to `ui/index.html`:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>QuotesDB</title>
|
||||
<link data-trunk rel="rust" data-bin="ui" />
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
```
|
||||
|
||||
Note: Added `data-bin="ui"` to the `<link>` tag so Trunk knows which binary to compile.
|
||||
|
||||
**Step 2: Write `Trunk.toml`**
|
||||
|
||||
```toml
|
||||
[build]
|
||||
target = "index.html"
|
||||
```
|
||||
|
||||
No `[build.cargo]` block needed — the `data-bin="ui"` attribute in `index.html` is sufficient to tell Trunk which binary to build.
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Consolidate docs
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/PLANNING.md`
|
||||
- Modify: `docs/ARCHITECTURE.md`
|
||||
|
||||
**Step 1: Update `docs/PLANNING.md`**
|
||||
|
||||
The sub-crate planning docs are all stubs with the same placeholder text. Replace the project-level `docs/PLANNING.md` with a consolidated version:
|
||||
|
||||
```markdown
|
||||
# quotesdb — Planning
|
||||
|
||||
## Development Phases
|
||||
|
||||
### Phase 0: Design (complete)
|
||||
|
||||
Finalized database schema, API endpoints, request/response shapes, auth model, and frontend routes.
|
||||
|
||||
### Phase 1: Structure refactor (complete)
|
||||
|
||||
Collapsed `api/`, `ui/`, and `tests/` sub-crates into a single `quotesdb` Cargo crate.
|
||||
- Single `Cargo.toml` at project root with two binaries: `api` and `ui`
|
||||
- Shared code lives in `src/lib.rs`
|
||||
- Integration tests live in `tests/` as standard Cargo integration tests
|
||||
- Trunk moved to project root
|
||||
|
||||
### Phase 2: Ticket Planning (pending)
|
||||
|
||||
Planning agents create nbd tickets for api, ui, and infra implementation work.
|
||||
|
||||
### Phase 3: Implementation (pending)
|
||||
|
||||
Implementation agents work through domain tickets.
|
||||
|
||||
## Work Log
|
||||
|
||||
- **2026-02-27** — Phase 0 complete. Project skeleton bootstrapped. Design doc written.
|
||||
- **2026-02-27** — Phase 1 complete. Refactored to single-crate layout (ticket b38032).
|
||||
```
|
||||
|
||||
**Step 2: Update `docs/ARCHITECTURE.md`**
|
||||
|
||||
```markdown
|
||||
# quotesdb — Architecture
|
||||
|
||||
## Component Overview
|
||||
|
||||
| Component | Path | Description |
|
||||
|-----------|------|-------------|
|
||||
| API binary | `src/bin/api/main.rs` | Rust/Axum backend on Cloudflare Workers. Handles all data operations via SQLx and Cloudflare D1. |
|
||||
| UI binary | `src/bin/ui/main.rs` | Yew (Rust/Wasm) frontend on Cloudflare Pages. Communicates with the API over HTTP. |
|
||||
| Shared library | `src/lib.rs` | Types and utilities shared between api and ui binaries. Must compile for both host and wasm32. |
|
||||
| Integration tests | `tests/` | Standard Cargo integration tests. Spin up the API against in-memory SQLite and make real HTTP requests. |
|
||||
| Infra | `infra/` | OpenTofu configuration for Cloudflare Worker, D1 database, and Pages project. |
|
||||
|
||||
## Component Interactions
|
||||
|
||||
```
|
||||
Browser
|
||||
└──> Cloudflare Pages (ui binary compiled to Wasm)
|
||||
└──> Cloudflare Workers (api binary)
|
||||
└──> Cloudflare D1 (SQLite)
|
||||
```
|
||||
|
||||
The UI is a static Wasm bundle served from Cloudflare Pages. It makes fetch requests to the Worker API, which reads and writes to a D1 database bound to the Worker.
|
||||
|
||||
Integration tests bypass the UI and talk directly to the API over HTTP, using a local in-memory SQLite database.
|
||||
|
||||
## Build Targets
|
||||
|
||||
| Artifact | Command | Compile target |
|
||||
|----------|---------|----------------|
|
||||
| API server | `cargo run` or `cargo build` | host (native) |
|
||||
| UI Wasm bundle | `trunk serve` or `trunk build` | `wasm32-unknown-unknown` |
|
||||
| Tests | `cargo test` | host (native) |
|
||||
|
||||
## Shared Code Constraints
|
||||
|
||||
`src/lib.rs` must compile for **both** `wasm32-unknown-unknown` (ui) and the host target (api). Avoid:
|
||||
- Threading primitives (`std::thread`, `std::sync::Mutex`)
|
||||
- Filesystem access (`std::fs`)
|
||||
- Any API that is not available in a Wasm environment
|
||||
|
||||
Use `#[cfg(not(target_arch = "wasm32"))]` and `#[cfg(target_arch = "wasm32")]` guards where needed.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Update project `README.md`
|
||||
|
||||
**Files:**
|
||||
- Modify: (check if `quotesdb/README.md` exists; if not, create it)
|
||||
|
||||
**Step 1: Check for existing README**
|
||||
|
||||
```sh
|
||||
ls README.md
|
||||
```
|
||||
|
||||
**Step 2: Write consolidated `README.md`**
|
||||
|
||||
```markdown
|
||||
# quotesdb
|
||||
|
||||
A quotes web application — browse, submit, and manage memorable quotes.
|
||||
|
||||
## What
|
||||
|
||||
quotesdb is a full-stack web application with:
|
||||
- A JSON REST API (`api` binary) backed by Cloudflare Workers + D1 (SQLite)
|
||||
- A Yew/Wasm frontend (`ui` binary) hosted on Cloudflare Pages
|
||||
- NanoID-identified quotes protected by a 4-word passphrase auth code
|
||||
|
||||
## How
|
||||
|
||||
Single Cargo crate with two binaries sharing common types via `src/lib.rs`:
|
||||
- `api`: Axum on Tokio, targeting Cloudflare Workers via workers-rs, SQLx + D1
|
||||
- `ui`: Yew compiled to `wasm32-unknown-unknown` via Trunk
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
# Start API server (local dev)
|
||||
cargo run
|
||||
|
||||
# Start UI dev server (requires wasm32 toolchain + trunk)
|
||||
trunk serve
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```sh
|
||||
cargo fmt && cargo check && cargo clippy && cargo test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of [Apache License, Version 2.0](../LICENSE-APACHE) or [MIT License](../LICENSE-MIT) at your option.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software was written with [Claude Code](https://claude.ai/claude-code) (claude-sonnet-4-6).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 11: Delete old sub-crate directories
|
||||
|
||||
**Step 1: Remove `api/`, `ui/`, `tests/` directories**
|
||||
|
||||
```sh
|
||||
rm -rf api/ ui/ tests/
|
||||
```
|
||||
|
||||
> **Warning:** Verify Task 5, 6, 7, and 8 are complete before running this. Double-check with `ls src/bin/api/main.rs src/bin/ui/main.rs index.html Trunk.toml`.
|
||||
|
||||
**Step 2: Verify the correct files remain**
|
||||
|
||||
```sh
|
||||
find . -name "*.rs" | grep -v target | sort
|
||||
```
|
||||
|
||||
Expected output:
|
||||
```
|
||||
./src/bin/api/main.rs
|
||||
./src/bin/ui/main.rs
|
||||
./src/lib.rs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 12: Run full validation
|
||||
|
||||
**Step 1: Format**
|
||||
|
||||
```sh
|
||||
cargo fmt
|
||||
```
|
||||
|
||||
Expected: exits 0.
|
||||
|
||||
**Step 2: Check**
|
||||
|
||||
```sh
|
||||
cargo check
|
||||
```
|
||||
|
||||
Expected: exits 0, no errors.
|
||||
|
||||
**Step 3: Clippy**
|
||||
|
||||
```sh
|
||||
cargo clippy
|
||||
```
|
||||
|
||||
Expected: exits 0, no warnings (or only acceptable ones).
|
||||
|
||||
**Step 4: Test**
|
||||
|
||||
```sh
|
||||
cargo test
|
||||
```
|
||||
|
||||
Expected: exits 0. Output: `running 0 tests` (stubs have no tests yet).
|
||||
|
||||
---
|
||||
|
||||
### Task 13: Update `CLAUDE.md`
|
||||
|
||||
**Files:**
|
||||
- Modify: `CLAUDE.md`
|
||||
|
||||
Update the `CLAUDE.md` to reflect the new single-crate structure. Key changes:
|
||||
1. Directory structure diagram — remove `api/`, `ui/`, `tests/` as sub-crates
|
||||
2. nbd note: tickets are scoped to the `quotesdb/` directory — always run `nbd` from there, not from parent directories
|
||||
3. Validation — commands run from `quotesdb/` root (no sub-directory `cd` needed)
|
||||
4. Agent dispatch — agents work in `src/bin/api/` or `src/bin/ui/`, not separate crates
|
||||
|
||||
See Task 14 for specific edits.
|
||||
|
||||
---
|
||||
|
||||
### Task 14: Commit
|
||||
|
||||
**Step 1: Stage all changes**
|
||||
|
||||
```sh
|
||||
git add Cargo.toml src/ index.html Trunk.toml docs/PLANNING.md docs/ARCHITECTURE.md README.md CLAUDE.md
|
||||
git status
|
||||
```
|
||||
|
||||
Verify no leftover `api/`, `ui/`, or `tests/` files appear.
|
||||
|
||||
**Step 2: Commit**
|
||||
|
||||
```sh
|
||||
git commit -m "refactor(quotesdb): collapse to single crate with api and ui binaries"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 15: Close ticket
|
||||
|
||||
**Step 1: Mark ticket done**
|
||||
|
||||
```sh
|
||||
nbd update b38032 --status done --json
|
||||
```
|
||||
|
||||
Expected: JSON with `"status": "done"`.
|
||||
@ -0,0 +1,9 @@
|
||||
//! API server binary entrypoint.
|
||||
//!
|
||||
//! Runs the quotesdb REST API. In production this targets Cloudflare Workers
|
||||
//! via workers-rs. For local development it runs a plain Axum/Tokio server.
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
@ -0,0 +1,9 @@
|
||||
//! UI binary entrypoint.
|
||||
//!
|
||||
//! Compiled to WebAssembly via Trunk targeting `wasm32-unknown-unknown`.
|
||||
//! Runs the Yew frontend application.
|
||||
|
||||
fn main() {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
@ -0,0 +1,8 @@
|
||||
//! Shared types and utilities for the `quotesdb` crate.
|
||||
//!
|
||||
//! This module is used by both the `api` and `ui` binaries.
|
||||
//! Code placed here must compile for both the host target (api)
|
||||
//! and `wasm32-unknown-unknown` (ui).
|
||||
//!
|
||||
//! Use `#[cfg(not(target_arch = "wasm32"))]` for host-only items
|
||||
//! and `#[cfg(target_arch = "wasm32")]` for wasm-only items.
|
||||
@ -1,5 +0,0 @@
|
||||
[package]
|
||||
name = "quotesdb-tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@ -1,25 +0,0 @@
|
||||
# quotesdb-tests
|
||||
|
||||
Integration test suite for the quotesdb service.
|
||||
|
||||
## What
|
||||
|
||||
HTTP-level integration tests that spin up the API server against an in-memory SQLite database and verify all endpoints.
|
||||
|
||||
## How
|
||||
|
||||
Separate Rust crate with tests in `tests/`. Each test suite covers a distinct endpoint or behaviour.
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
cargo test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of [Apache License, Version 2.0](../../LICENSE-APACHE) or [MIT License](../../LICENSE-MIT) at your option.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software was written with [Claude Code](https://claude.ai/claude-code) (claude-sonnet-4-6).
|
||||
@ -1,9 +0,0 @@
|
||||
# quotesdb-tests — Planning
|
||||
|
||||
## Development Phases
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
|
||||
## Work Log
|
||||
|
||||
_Updated as work progresses._
|
||||
@ -1,14 +0,0 @@
|
||||
[package]
|
||||
name = "quotesdb-ui"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
common = { path = "../../common" }
|
||||
|
||||
[profile.release]
|
||||
opt-level = "z"
|
||||
lto = true
|
||||
strip = true
|
||||
codegen-units = 1
|
||||
@ -1,31 +0,0 @@
|
||||
# quotesdb-ui
|
||||
|
||||
Yew (Rust/Wasm) frontend for the quotesdb quotes service, hosted on Cloudflare Pages.
|
||||
|
||||
## What
|
||||
|
||||
A web UI for browsing, submitting, and managing quotes. Compiled to WebAssembly from Rust using the Yew framework.
|
||||
|
||||
## How
|
||||
|
||||
Built with Yew, compiled to `wasm32-unknown-unknown` via Trunk. Communicates with the quotesdb-api backend.
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
trunk serve
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```sh
|
||||
cargo fmt && cargo check && cargo clippy && cargo test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of [Apache License, Version 2.0](../../LICENSE-APACHE) or [MIT License](../../LICENSE-MIT) at your option.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This software was written with [Claude Code](https://claude.ai/claude-code) (claude-sonnet-4-6).
|
||||
@ -1,9 +0,0 @@
|
||||
# quotesdb-ui — Architecture
|
||||
|
||||
## Component Overview
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
|
||||
## Component Interactions
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
@ -1,9 +0,0 @@
|
||||
# quotesdb-ui — Planning
|
||||
|
||||
## Development Phases
|
||||
|
||||
_To be filled in during Phase 1 planning._
|
||||
|
||||
## Work Log
|
||||
|
||||
_Updated as work progresses._
|
||||
@ -1 +0,0 @@
|
||||
fn main() {}
|
||||
@ -1,2 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod tests {}
|
||||
Loading…
Reference in New Issue