@dewtech/dare-cli 2.7.0 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -3
- package/dist/__tests__/dag-runner/ralph-loop.test.js +5 -0
- package/dist/__tests__/dag-runner/ralph-loop.test.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +19 -2
- package/dist/commands/init.js.map +1 -1
- package/dist/core/types/project.d.ts +1 -1
- package/dist/core/types/project.d.ts.map +1 -1
- package/dist/dag-runner/ralph-loop.d.ts.map +1 -1
- package/dist/dag-runner/ralph-loop.js +15 -0
- package/dist/dag-runner/ralph-loop.js.map +1 -1
- package/dist/utils/project-generator.js +96 -1
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/stack-bootstrap.d.ts +2 -2
- package/dist/utils/stack-bootstrap.d.ts.map +1 -1
- package/dist/utils/stack-bootstrap.js +495 -3
- package/dist/utils/stack-bootstrap.js.map +1 -1
- package/dist/utils/templates.d.ts.map +1 -1
- package/dist/utils/templates.js +49 -15
- package/dist/utils/templates.js.map +1 -1
- package/package.json +1 -1
- package/templates/frontend/leptos-csr/.cargo/config.toml +2 -0
- package/templates/frontend/leptos-csr/Cargo.toml +16 -0
- package/templates/frontend/leptos-csr/Trunk.toml +10 -0
- package/templates/frontend/leptos-csr/index.html +11 -0
- package/templates/frontend/leptos-csr/src/lib.rs +20 -0
- package/templates/frontend/leptos-csr/style/main.scss +19 -0
- package/templates/frontend/leptos-fullstack/.cargo/config.toml +4 -0
- package/templates/frontend/leptos-fullstack/Cargo.toml +56 -0
- package/templates/frontend/leptos-fullstack/src/app.rs +49 -0
- package/templates/frontend/leptos-fullstack/src/lib.rs +9 -0
- package/templates/frontend/leptos-fullstack/src/main.rs +29 -0
- package/templates/frontend/leptos-fullstack/style/main.scss +19 -0
- package/templates/ide/antigravity/.agents/skills/dare-rust-workspace/SKILL.md +275 -0
- package/templates/ide/claude/.claude/commands/dare-rust-leptos.md +269 -0
- package/templates/ide/claude/.claude/commands/dare-rust-workspace.md +209 -0
- package/templates/ide/claude/CLAUDE.md +10 -1
- package/templates/ide/cursor/.cursor/rules/skill-rust-workspace.mdc +312 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
# /dare-rust-leptos
|
|
2
|
+
|
|
3
|
+
Guia de desenvolvimento Leptos para projetos DARE. Cobre decisão de variante, idioms obrigatórios, antipatterns, tipos compartilhados e templates de tasks.
|
|
4
|
+
|
|
5
|
+
## Como usar
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
/dare-rust-leptos ← decide variante + configura workspace
|
|
9
|
+
/dare-rust-leptos --csr ← guia específico para CSR + trunk
|
|
10
|
+
/dare-rust-leptos --fullstack ← guia específico para SSR + cargo-leptos
|
|
11
|
+
/dare-rust-leptos --shared-types ← padrão cfg_attr para tipos server + WASM
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Decisão de variante: CSR vs Fullstack
|
|
17
|
+
|
|
18
|
+
| Critério | CSR (trunk) | Fullstack (cargo-leptos) |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| SEO necessário | ❌ | ✅ |
|
|
21
|
+
| Time-to-interactive crítico | ❌ | ✅ |
|
|
22
|
+
| Dashboard interno / admin | ✅ | ✅ |
|
|
23
|
+
| Backend Axum existente | indiferente | ✅ integração direta |
|
|
24
|
+
| Simplicidade de deploy | ✅ arquivos estáticos (CDN) | ⚠️ binário Axum |
|
|
25
|
+
| Server functions (`#[server]`) | ❌ não existe | ✅ |
|
|
26
|
+
|
|
27
|
+
**Regra de ouro:**
|
|
28
|
+
- Se o projeto vive atrás de login e SEO não importa → **CSR**
|
|
29
|
+
- Se precisa de SEO, carregamento inicial rápido, ou server functions → **Fullstack**
|
|
30
|
+
- Se já tem `rust-axum` como backend no monorepo → **Fullstack** (workspace unificado)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2. Ferramentas — nunca misturar
|
|
35
|
+
|
|
36
|
+
| Variante | Build | Dev server | Test |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| CSR | `trunk build --release` | `trunk serve` | `cargo test --workspace` |
|
|
39
|
+
| Fullstack | `cargo leptos build --release` | `cargo leptos watch` | `cargo test --workspace` |
|
|
40
|
+
|
|
41
|
+
> ⚠️ `cargo leptos test` **não existe**. Use sempre `cargo test --workspace`.
|
|
42
|
+
> ⚠️ Não use `trunk` para fullstack nem `cargo leptos` para CSR — ferramentas erradas para o target errado.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 3. Idioms obrigatórios Leptos 0.7
|
|
47
|
+
|
|
48
|
+
### Estado reativo
|
|
49
|
+
```rust
|
|
50
|
+
// ✅ Fine-grained signals — só re-renderiza o que usa o signal
|
|
51
|
+
let (count, set_count) = signal(0);
|
|
52
|
+
let doubled = move || count.get() * 2; // derived (memo inline)
|
|
53
|
+
|
|
54
|
+
// Para estado complexo ou compartilhado entre componentes:
|
|
55
|
+
let count = RwSignal::new(0); // read + write em um só
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Dados assíncronos
|
|
59
|
+
```rust
|
|
60
|
+
// ✅ Resource — fetch declarativo, integra com Suspense
|
|
61
|
+
let user = Resource::new(|| user_id(), |id| async move { fetch_user(id).await });
|
|
62
|
+
|
|
63
|
+
// ✅ Suspense — loading state automático
|
|
64
|
+
view! {
|
|
65
|
+
<Suspense fallback=|| view! { <p>"Loading..."</p> }>
|
|
66
|
+
{move || user.get().map(|u| view! { <p>{u.name}</p> })}
|
|
67
|
+
</Suspense>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ❌ Nunca — Effect que faz fetch (re-executa em todo render)
|
|
71
|
+
Effect::new(move |_| { spawn_local(async { fetch_user(id).await }); });
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Mutações
|
|
75
|
+
```rust
|
|
76
|
+
// ✅ Action — para submits, forms, operações que mudam estado
|
|
77
|
+
let save = Action::new(|input: &String| {
|
|
78
|
+
let input = input.clone();
|
|
79
|
+
async move { api::save(input).await }
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
view! {
|
|
83
|
+
<button on:click=move |_| save.dispatch("hello".to_string())>
|
|
84
|
+
"Save"
|
|
85
|
+
</button>
|
|
86
|
+
<Show when=move || save.pending().get()>
|
|
87
|
+
<p>"Saving..."</p>
|
|
88
|
+
</Show>
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Renderização condicional e listas
|
|
93
|
+
```rust
|
|
94
|
+
// ✅ Show — condicional com lazy evaluation
|
|
95
|
+
view! {
|
|
96
|
+
<Show when=move || logged_in.get() fallback=|| view! { <Login/> }>
|
|
97
|
+
<Dashboard/>
|
|
98
|
+
</Show>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ✅ For — lista reativa com key para reconciliação eficiente
|
|
102
|
+
view! {
|
|
103
|
+
<For
|
|
104
|
+
each=move || items.get()
|
|
105
|
+
key=|item| item.id
|
|
106
|
+
children=move |item| view! { <ItemRow item=item/> }
|
|
107
|
+
/>
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Server functions (fullstack only)
|
|
112
|
+
```rust
|
|
113
|
+
// ✅ #[server] macro — compila para HTTP call no client, fn real no server
|
|
114
|
+
#[server(SaveUser, "/api")]
|
|
115
|
+
pub async fn save_user(name: String) -> Result<User, ServerFnError> {
|
|
116
|
+
// Este código só roda no server (feature = "ssr")
|
|
117
|
+
let user = db::create_user(name).await?;
|
|
118
|
+
Ok(user)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// No componente — usa como Action normal
|
|
122
|
+
let save = ServerAction::<SaveUser>::new();
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 4. Tipos compartilhados server + WASM
|
|
128
|
+
|
|
129
|
+
Tipos que precisam existir tanto no server (SQLx, Axum) quanto no client (WASM) usam `cfg_attr`:
|
|
130
|
+
|
|
131
|
+
```rust
|
|
132
|
+
// crates/<projeto>-domain/src/lib.rs (ou src/models.rs)
|
|
133
|
+
use serde::{Deserialize, Serialize};
|
|
134
|
+
|
|
135
|
+
#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))] // só no server
|
|
136
|
+
#[derive(Clone, Debug, Serialize, Deserialize)] // server + WASM
|
|
137
|
+
pub struct SecurityEvent {
|
|
138
|
+
pub id: uuid::Uuid,
|
|
139
|
+
pub attack_type: String,
|
|
140
|
+
pub risk_score: f32,
|
|
141
|
+
pub blocked: bool,
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Cargo.toml do crate domain:**
|
|
146
|
+
```toml
|
|
147
|
+
[dependencies]
|
|
148
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
149
|
+
uuid = { version = "1.10", features = ["v4", "serde"] }
|
|
150
|
+
|
|
151
|
+
[dependencies.sqlx]
|
|
152
|
+
version = "0.8"
|
|
153
|
+
features = ["postgres", "runtime-tokio", "uuid"]
|
|
154
|
+
optional = true
|
|
155
|
+
|
|
156
|
+
[features]
|
|
157
|
+
ssr = ["dep:sqlx"]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
> O crate domain é compilado duas vezes: uma para x86 (server, com sqlx) e uma para wasm32 (client, sem sqlx). O `cfg_attr` garante que `sqlx::FromRow` só aparece na compilação server.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 5. Configuração workspace misto (WASM + nativo)
|
|
165
|
+
|
|
166
|
+
Quando o workspace tem crates Leptos/WASM **e** crates nativos (ex: `napi-rs`, `aya`, Axum):
|
|
167
|
+
|
|
168
|
+
```toml
|
|
169
|
+
# .cargo/config.toml na raiz do workspace
|
|
170
|
+
# NÃO definir [build] target global aqui.
|
|
171
|
+
# Cada crate define seu próprio target via features e cargo-leptos/trunk.
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```toml
|
|
175
|
+
# Cargo.toml do workspace
|
|
176
|
+
[workspace]
|
|
177
|
+
resolver = "2"
|
|
178
|
+
members = [
|
|
179
|
+
"crates/ars-core", # lib nativa (x86)
|
|
180
|
+
"crates/ars-server", # bin nativo — Axum
|
|
181
|
+
"crates/ars-web", # bin+lib WASM — Leptos
|
|
182
|
+
"crates/ars-cli", # bin nativo
|
|
183
|
+
]
|
|
184
|
+
# crates napi-rs ficam em workspace separado ou excluídos do members padrão
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```toml
|
|
188
|
+
# crates/ars-web/Cargo.toml — features separam server de WASM
|
|
189
|
+
[features]
|
|
190
|
+
default = []
|
|
191
|
+
hydrate = ["leptos/hydrate"]
|
|
192
|
+
ssr = [
|
|
193
|
+
"dep:axum",
|
|
194
|
+
"dep:leptos_axum",
|
|
195
|
+
"dep:tokio",
|
|
196
|
+
"leptos/ssr",
|
|
197
|
+
]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
> ⚠️ **Antipattern crítico**: adicionar `[build] target = "wasm32-unknown-unknown"` no `.cargo/config.toml` raiz quebra todos os crates nativos. `cargo leptos` e `trunk` gerenciam o target WASM internamente — não interfira.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 6. Antipatterns a evitar
|
|
205
|
+
|
|
206
|
+
| Antipattern | Por quê | Alternativa |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `wasm_bindgen` direto | Bypassa abstrações do Leptos, código frágil | Use APIs do Leptos (`web_sys` via feature quando necessário) |
|
|
209
|
+
| `panic!` em componentes | Derruba o app inteiro sem `ErrorBoundary` | `Result<_, ServerFnError>` + `ErrorBoundary` |
|
|
210
|
+
| `Effect` que faz fetch | Re-executa a cada render, difícil de cancelar | `Resource::new()` |
|
|
211
|
+
| `tokio::spawn` no client | `tokio` não existe no WASM | `spawn_local()` (wasm) ou só em server functions |
|
|
212
|
+
| `std::thread` no client | Não existe no WASM | Leptos signals para paralelismo reativo |
|
|
213
|
+
| `cargo leptos test` | Não existe — comando inválido | `cargo test --workspace` |
|
|
214
|
+
| `[build] target` global | Quebra crates nativos no workspace misto | Sem target global; cargo-leptos gerencia internamente |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 7. Templates de tasks DARE para Leptos
|
|
219
|
+
|
|
220
|
+
Cole no `DARE/dare-dag.yaml` após gerar o blueprint:
|
|
221
|
+
|
|
222
|
+
```yaml
|
|
223
|
+
# Task 1 — Workspace + AppShell + Router
|
|
224
|
+
- id: leptos-001
|
|
225
|
+
title: "Workspace, AppShell e Router base"
|
|
226
|
+
description: |
|
|
227
|
+
Configurar Cargo workspace com resolver = "2".
|
|
228
|
+
Criar App component com leptos_router::Router.
|
|
229
|
+
Criar layout base (header, main, footer).
|
|
230
|
+
Rota "/" → HomePage component.
|
|
231
|
+
Ralph Loop: cargo leptos build --release + cargo test --workspace + cargo clippy
|
|
232
|
+
depends_on: []
|
|
233
|
+
|
|
234
|
+
# Task 2 — Form com Action + server function
|
|
235
|
+
- id: leptos-002
|
|
236
|
+
title: "Form com Action e validação server-side"
|
|
237
|
+
description: |
|
|
238
|
+
Criar #[server] fn para processar o form.
|
|
239
|
+
Criar componente com ActionForm ou Action manual.
|
|
240
|
+
Validar input no server (retorna ServerFnError em caso de erro).
|
|
241
|
+
Exibir pending state com Action::pending() signal.
|
|
242
|
+
Ralph Loop: cargo leptos build --release + cargo test --workspace
|
|
243
|
+
depends_on: [leptos-001]
|
|
244
|
+
|
|
245
|
+
# Task 3 — Lista paginada com Resource + Suspense
|
|
246
|
+
- id: leptos-003
|
|
247
|
+
title: "Lista paginada com Resource, Suspense e error handling"
|
|
248
|
+
description: |
|
|
249
|
+
Criar Resource que recebe página como signal de parâmetro.
|
|
250
|
+
Envolver em Suspense com fallback de loading.
|
|
251
|
+
Usar ErrorBoundary para erros de fetch.
|
|
252
|
+
Adicionar paginação com For component e key por ID.
|
|
253
|
+
Ralph Loop: cargo leptos build --release + cargo test --workspace
|
|
254
|
+
depends_on: [leptos-001]
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## 8. O que fazer agora
|
|
260
|
+
|
|
261
|
+
1. **Leia `DARE/DESIGN.md`** para entender quais componentes e server functions são necessários
|
|
262
|
+
2. **Rode `cargo leptos watch`** (fullstack) ou **`trunk serve`** (CSR) para confirmar que o scaffold compila
|
|
263
|
+
3. **Gere `DARE/BLUEPRINT.md`** com `/dare-blueprint` — inclua:
|
|
264
|
+
- Tabela de componentes (nome, props, signals usados)
|
|
265
|
+
- Lista de server functions com assinatura
|
|
266
|
+
- Tipos compartilhados no crate domain
|
|
267
|
+
4. **Use `/dare-rust-workspace`** se precisar decidir estrutura multi-crate
|
|
268
|
+
|
|
269
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# /dare-rust-workspace
|
|
2
|
+
|
|
3
|
+
Decisão e migração de Cargo workspace multi-crate para projetos Rust/Axum.
|
|
4
|
+
Cobre dois cenários:
|
|
5
|
+
|
|
6
|
+
- **Cenário A — Design/Blueprint:** decidir desde o início se o projeto
|
|
7
|
+
nasce single-crate ou workspace.
|
|
8
|
+
- **Cenário B — Migração:** propor plano de PRs incrementais para
|
|
9
|
+
transformar um projeto single-crate maduro em workspace.
|
|
10
|
+
|
|
11
|
+
## Como usar
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
/dare-rust-workspace # análise contextual (lê BLUEPRINT/src/)
|
|
15
|
+
/dare-rust-workspace --design # foco em decisão para projeto novo
|
|
16
|
+
/dare-rust-workspace --migrate # foco em plano de migração
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## O que fazer
|
|
20
|
+
|
|
21
|
+
### 1) Detectar o cenário
|
|
22
|
+
|
|
23
|
+
- Se existe `DARE/BLUEPRINT.md` mas o `src/` ainda não existe ou tem < 5
|
|
24
|
+
arquivos → Cenário A (decisão)
|
|
25
|
+
- Se `src/` tem > 30 arquivos `.rs` ou múltiplas subpastas top-level →
|
|
26
|
+
Cenário B (migração)
|
|
27
|
+
- Caso intermediário (15–30 arquivos) → mostrar os critérios e perguntar
|
|
28
|
+
ao usuário
|
|
29
|
+
|
|
30
|
+
### 2) Cenário A — Decisão na fase Design/Blueprint
|
|
31
|
+
|
|
32
|
+
Aplique os critérios:
|
|
33
|
+
|
|
34
|
+
**Comece single-crate quando TODOS forem verdadeiros:**
|
|
35
|
+
- Apenas 1 binário (HTTP server)
|
|
36
|
+
- < 30 arquivos `.rs` esperados
|
|
37
|
+
- 1–2 sistemas externos (DB; ou DB + cache)
|
|
38
|
+
- Equipe ≤ 2 devs
|
|
39
|
+
- Sem deploy independente para subcomponentes
|
|
40
|
+
|
|
41
|
+
**Comece workspace quando QUALQUER for verdadeiro:**
|
|
42
|
+
- ≥ 2 binários previstos (API + worker; API + admin)
|
|
43
|
+
- ≥ 3 sistemas externos (PG + Redis + Rabbit + Qdrant + Neo4j…)
|
|
44
|
+
- Deploy independente desejado (workers em pods separados)
|
|
45
|
+
- Fronteiras arquiteturais críticas (domain puro; SDK publicável)
|
|
46
|
+
- Equipe ≥ 3 devs em paralelo
|
|
47
|
+
|
|
48
|
+
**Layout recomendado para workspace** (use `<p>` como prefixo do projeto):
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
projeto/
|
|
52
|
+
├── Cargo.toml # workspace root
|
|
53
|
+
├── crates/
|
|
54
|
+
│ ├── <p>-domain/ (lib) # entities puras
|
|
55
|
+
│ ├── <p>-config/ (lib)
|
|
56
|
+
│ ├── <p>-db/ (lib)
|
|
57
|
+
│ ├── <p>-cache/ (lib)
|
|
58
|
+
│ ├── <p>-queue/ (lib)
|
|
59
|
+
│ ├── <p>-services/ (lib) # business logic
|
|
60
|
+
│ ├── <p>-integrators/ (lib) # clientes externos
|
|
61
|
+
│ ├── <p>-api/ (bin+lib) # HTTP server
|
|
62
|
+
│ ├── <p>-worker-<X>/ (bin) # 1 binário por worker
|
|
63
|
+
│ └── <p>-e2e/ (tests)
|
|
64
|
+
└── xtask/ # scripts em Rust
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**`Cargo.toml` raiz:**
|
|
68
|
+
```toml
|
|
69
|
+
[workspace]
|
|
70
|
+
resolver = "2"
|
|
71
|
+
members = ["crates/<p>-domain", "crates/<p>-services", "crates/<p>-api", ...]
|
|
72
|
+
|
|
73
|
+
[workspace.package]
|
|
74
|
+
version = "0.1.0"
|
|
75
|
+
edition = "2021"
|
|
76
|
+
rust-version = "1.80"
|
|
77
|
+
|
|
78
|
+
[workspace.dependencies]
|
|
79
|
+
tokio = { version = "1.40", features = ["full"] }
|
|
80
|
+
axum = { version = "0.7", features = ["macros"] }
|
|
81
|
+
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono", "migrate"] }
|
|
82
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
83
|
+
thiserror = "1.0"
|
|
84
|
+
anyhow = "1.0"
|
|
85
|
+
tracing = "0.1"
|
|
86
|
+
uuid = { version = "1.10", features = ["v4", "serde"] }
|
|
87
|
+
chrono = { version = "0.4", features = ["serde"] }
|
|
88
|
+
|
|
89
|
+
[profile.release]
|
|
90
|
+
opt-level = 3
|
|
91
|
+
lto = "thin"
|
|
92
|
+
codegen-units = 1
|
|
93
|
+
strip = true
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Cada crate filho usa `<dep> = { workspace = true }` para herdar versão.
|
|
97
|
+
|
|
98
|
+
**Diagrama no BLUEPRINT.md (Mermaid):**
|
|
99
|
+
```mermaid
|
|
100
|
+
graph TD
|
|
101
|
+
api[<p>-api]
|
|
102
|
+
worker[<p>-worker-flow]
|
|
103
|
+
services[<p>-services]
|
|
104
|
+
domain[<p>-domain]
|
|
105
|
+
db[<p>-db]
|
|
106
|
+
api --> services
|
|
107
|
+
worker --> services
|
|
108
|
+
services --> db
|
|
109
|
+
services --> domain
|
|
110
|
+
db --> domain
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Regra de seta: domain no fundo (ninguém depende para baixo), services no
|
|
114
|
+
meio (depende de domain/db, nunca de api), binários no topo.
|
|
115
|
+
|
|
116
|
+
### 3) Cenário B — Plano de migração em 4 PRs
|
|
117
|
+
|
|
118
|
+
Migração **nunca é big-bang**. Cada PR passa build/test/clippy no fim e é
|
|
119
|
+
deployável.
|
|
120
|
+
|
|
121
|
+
**PR 1 — Workers** (mais isolados, menor risco):
|
|
122
|
+
```
|
|
123
|
+
src/workers/ → crates/<p>-worker-<X>/
|
|
124
|
+
├── Cargo.toml
|
|
125
|
+
└── src/main.rs
|
|
126
|
+
```
|
|
127
|
+
- Cada `tokio::spawn(worker_x)` do `main.rs` da API vira binário próprio.
|
|
128
|
+
- API para de spawn workers; workers sobem como processo independente.
|
|
129
|
+
- `docker-compose.yml` ganha service novo por worker.
|
|
130
|
+
- Em k8s: Deployment próprio com scaling independente.
|
|
131
|
+
|
|
132
|
+
**PR 2 — Integrators** (clientes externos):
|
|
133
|
+
```
|
|
134
|
+
src/integrators/{llm, neo4j, qdrant}.rs → crates/<p>-integrators/src/lib.rs
|
|
135
|
+
```
|
|
136
|
+
- Tudo que fala com mundo externo (LLM, Stripe, Meta, DB externo) vira lib.
|
|
137
|
+
- API e workers importam via `path = "../<p>-integrators"`.
|
|
138
|
+
- Crate é folha do grafo — NÃO depende de domain/services.
|
|
139
|
+
|
|
140
|
+
**PR 3 — Domain** (entidades puras):
|
|
141
|
+
```
|
|
142
|
+
src/models/ \
|
|
143
|
+
src/dto/ → crates/<p>-domain/src/lib.rs
|
|
144
|
+
src/error.rs /
|
|
145
|
+
```
|
|
146
|
+
- `<p>-domain` tem deps **mínimas**: `serde`, `uuid`, `chrono`, `thiserror`.
|
|
147
|
+
- Nada de axum, sqlx, redis. Build do domain falha se alguém tentar.
|
|
148
|
+
- Outros crates passam a usar `<p>-domain` em vez de `crate::models::`.
|
|
149
|
+
|
|
150
|
+
**PR 4 — API e workspace root:**
|
|
151
|
+
```
|
|
152
|
+
Cargo.toml raiz → [workspace] puro
|
|
153
|
+
src/ (resto) → crates/<p>-api/src/
|
|
154
|
+
```
|
|
155
|
+
- `Cargo.toml` raiz só com `[workspace]` + `[workspace.package]` +
|
|
156
|
+
`[workspace.dependencies]` + `[profile.*]`.
|
|
157
|
+
- API vai pra `crates/<p>-api/`.
|
|
158
|
+
- Todas as deps centralizadas no root.
|
|
159
|
+
|
|
160
|
+
### 4) Validação após cada PR
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
cargo build --workspace --all-targets
|
|
164
|
+
cargo test --workspace
|
|
165
|
+
cargo clippy --workspace --all-targets -- -D warnings
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Mais smoke E2E do projeto. Se algum falhar, PR está incompleto.
|
|
169
|
+
|
|
170
|
+
### 5) Tradução de imports
|
|
171
|
+
|
|
172
|
+
| Antes (single-crate) | Depois (workspace) |
|
|
173
|
+
|----------------------|--------------------|
|
|
174
|
+
| `use crate::models::User;` | `use <p>_domain::User;` |
|
|
175
|
+
| `use crate::services::auth::login;` | `use <p>_services::auth::login;` |
|
|
176
|
+
| `use crate::integrators::llm::Gemini;` | `use <p>_integrators::llm::Gemini;` |
|
|
177
|
+
| `use crate::handlers::health::router;` | *fica igual — mesmo crate* |
|
|
178
|
+
|
|
179
|
+
Hifens (Cargo) viram underlines (Rust idents).
|
|
180
|
+
|
|
181
|
+
### 6) Antipatterns para alertar o usuário
|
|
182
|
+
|
|
183
|
+
| Antipattern | Por quê |
|
|
184
|
+
|-------------|---------|
|
|
185
|
+
| Big-bang (1 PR mexe em tudo) | Impossível revisar/reverter |
|
|
186
|
+
| Crate `common`/`shared`/`utils` | Vira lixeira; viola SRP |
|
|
187
|
+
| Crate por arquivo | 50 crates de 100 linhas → parar build |
|
|
188
|
+
| Refactor + migração no mesmo PR | Bug fica escondido |
|
|
189
|
+
| Mover testes em PR separado | Crate sem test = bug em standby |
|
|
190
|
+
| Esquecer atualizar `xtask`/CI | Pipeline quebra |
|
|
191
|
+
|
|
192
|
+
### 7) Quando NÃO migrar
|
|
193
|
+
|
|
194
|
+
- Projeto < 30 arquivos, 1 binário, 1 dev
|
|
195
|
+
- Sprint crítico em curso (migração é refactor estrutural)
|
|
196
|
+
- Sem sinais reais de dor (build < 5s, sem conflitos)
|
|
197
|
+
|
|
198
|
+
## Saída esperada
|
|
199
|
+
|
|
200
|
+
Para Cenário A: atualizar `DARE/BLUEPRINT.md` com a estrutura de crates
|
|
201
|
+
(diagrama + tabela de responsabilidades + `Cargo.toml` workspace).
|
|
202
|
+
|
|
203
|
+
Para Cenário B: gerar arquivo `DARE/MIGRATION-PLAN.md` com:
|
|
204
|
+
- Sintomas detectados (linhas de código, arquivos, etc.)
|
|
205
|
+
- 4 PRs em ordem com diff de pastas
|
|
206
|
+
- Critério de aceite por PR
|
|
207
|
+
- Estimativa de esforço
|
|
208
|
+
|
|
209
|
+
$ARGUMENTS
|
|
@@ -12,7 +12,7 @@ Você é o Claude Code, assistente de desenvolvimento seguindo o método DARE:
|
|
|
12
12
|
- Atualize o status em `DARE/TASKS.md` ao concluir cada task
|
|
13
13
|
- Nunca pule o Ralph Loop (build → test → lint) antes de marcar uma task como DONE
|
|
14
14
|
- Aprovação humana obrigatória antes de merge para a branch principal
|
|
15
|
-
- Use os slash commands `/dare-design`, `/dare-blueprint`, `/dare-execute`, `/dare-tasks`
|
|
15
|
+
- Use os slash commands `/dare-design`, `/dare-blueprint`, `/dare-execute`, `/dare-tasks`, `/dare-rust-leptos`, `/dare-rust-workspace`
|
|
16
16
|
|
|
17
17
|
## Estrutura do Projeto
|
|
18
18
|
```
|
|
@@ -78,6 +78,15 @@ dare execute --parallel # executa tasks em paralelo respeitando depends_on
|
|
|
78
78
|
- Pinia para state management
|
|
79
79
|
- Ralph Loop: `npm run build && npm test && npx eslint src`
|
|
80
80
|
|
|
81
|
+
### Leptos (Rust → WASM) — v2.10+
|
|
82
|
+
- Dois modos: `rust-leptos` (SSR + Axum, cargo-leptos) e `rust-leptos-csr` (WASM puro, trunk)
|
|
83
|
+
- `#[component]` macro, signals reativos, `Resource` para async, `Action` para mutações
|
|
84
|
+
- **Nunca** usar `cargo leptos test` — use `cargo test --workspace`
|
|
85
|
+
- **Nunca** definir `[build] target` global no `.cargo/config.toml` (quebra workspace misto)
|
|
86
|
+
- Ralph Loop fullstack: `cargo leptos build --release && cargo test --workspace && cargo clippy --all-features -- -D warnings`
|
|
87
|
+
- Ralph Loop CSR: `trunk build --release && cargo test --workspace && cargo clippy --all-features -- -D warnings`
|
|
88
|
+
- Use `/dare-rust-leptos` para guia completo de idioms, tipos compartilhados e tasks
|
|
89
|
+
|
|
81
90
|
## Knowledge Graph (GraphRAG)
|
|
82
91
|
|
|
83
92
|
O projeto mantém um grafo de conhecimento em `dare-graph.yml`:
|