@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
package/dist/utils/templates.js
CHANGED
|
@@ -40,6 +40,19 @@ export function generateCursorRules(config) {
|
|
|
40
40
|
- Use Pinia for state management
|
|
41
41
|
- Use Vue Router for navigation
|
|
42
42
|
- Write Vitest + Vue Test Utils tests`,
|
|
43
|
+
'rust-leptos': `## Frontend: Leptos fullstack (Rust SSR + WASM)
|
|
44
|
+
- Use \`#[component]\` macro — no class components
|
|
45
|
+
- State: \`signal()\`, \`Resource\` for async, \`Action\` for mutations
|
|
46
|
+
- Loading: \`Suspense\`/\`Transition\` — never block the render
|
|
47
|
+
- Server functions: \`#[server]\` macro (ssr feature only)
|
|
48
|
+
- Shared types: \`#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]\`
|
|
49
|
+
- Avoid: \`tokio::spawn\` on client, \`panic!\` in components, \`Effect\` for fetch
|
|
50
|
+
- Build: \`cargo leptos build\` | Test: \`cargo test --workspace\` | Dev: \`cargo leptos watch\``,
|
|
51
|
+
'rust-leptos-csr': `## Frontend: Leptos CSR (Rust WASM + trunk)
|
|
52
|
+
- Pure client-side WASM — no SSR, no \`#[server]\` functions
|
|
53
|
+
- Use \`#[component]\` macro, \`signal()\` for state, \`Resource\` for async
|
|
54
|
+
- Build tool: \`trunk build\` (NOT cargo leptos) | Dev: \`trunk serve\`
|
|
55
|
+
- Test: \`cargo test --workspace\` | Deploy: \`dist/\` is fully static`,
|
|
43
56
|
};
|
|
44
57
|
return `# DARE Framework - Cursor Rules
|
|
45
58
|
|
|
@@ -236,6 +249,17 @@ export function generateClaudeCodeRules(config) {
|
|
|
236
249
|
- TypeScript for all components
|
|
237
250
|
- Use Pinia for state management
|
|
238
251
|
- Ralph Loop: \`npm run build && npm test && npx eslint src\``,
|
|
252
|
+
'rust-leptos': `## Frontend: Leptos fullstack (Rust SSR + WASM)
|
|
253
|
+
- Use \`#[component]\` macro — estado com \`signal()\`, async com \`Resource\`, mutações com \`Action\`
|
|
254
|
+
- Loading: \`Suspense\`/\`Transition\` — nunca bloqueie o render
|
|
255
|
+
- Server functions: \`#[server]\` macro (feature ssr)
|
|
256
|
+
- Tipos compartilhados server + WASM: \`#[cfg_attr(feature = "ssr", derive(sqlx::FromRow))]\`
|
|
257
|
+
- Evite: \`tokio::spawn\` no client, \`panic!\` em componentes, \`Effect\` para fetch
|
|
258
|
+
- Ralph Loop: \`cargo leptos build --release && cargo test --workspace && cargo clippy --all-features -- -D warnings\``,
|
|
259
|
+
'rust-leptos-csr': `## Frontend: Leptos CSR (Rust WASM + trunk)
|
|
260
|
+
- WASM puro — sem SSR, sem \`#[server]\`
|
|
261
|
+
- Build: \`trunk build --release\` | Dev: \`trunk serve\` | Test: \`cargo test --workspace\`
|
|
262
|
+
- Ralph Loop: \`trunk build --release && cargo test --workspace && cargo clippy --all-features -- -D warnings\``,
|
|
239
263
|
};
|
|
240
264
|
return `# DARE Framework
|
|
241
265
|
|
|
@@ -447,21 +471,31 @@ Antes de marcar qualquer task como DONE:
|
|
|
447
471
|
`;
|
|
448
472
|
}
|
|
449
473
|
export function generateClaudeSettings(stack) {
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
474
|
+
const isLeptosFullstack = stack.frontend === 'rust-leptos';
|
|
475
|
+
const isLeptosCsr = stack.frontend === 'rust-leptos-csr';
|
|
476
|
+
const buildCmd = isLeptosFullstack
|
|
477
|
+
? 'cargo leptos build --release'
|
|
478
|
+
: isLeptosCsr
|
|
479
|
+
? 'trunk build --release'
|
|
480
|
+
: stack.backend === 'rust-axum'
|
|
481
|
+
? 'cargo build'
|
|
482
|
+
: stack.backend === 'python-fastapi' || stack.structure === 'mcp-server'
|
|
483
|
+
? 'python -m py_compile main.py'
|
|
484
|
+
: 'npm run build';
|
|
485
|
+
const testCmd = isLeptosFullstack || isLeptosCsr
|
|
486
|
+
? 'cargo test --workspace'
|
|
487
|
+
: stack.backend === 'rust-axum'
|
|
488
|
+
? 'cargo test'
|
|
489
|
+
: stack.backend === 'python-fastapi' || stack.structure === 'mcp-server'
|
|
490
|
+
? 'pytest'
|
|
491
|
+
: 'npm test';
|
|
492
|
+
const lintCmd = isLeptosFullstack || isLeptosCsr
|
|
493
|
+
? 'cargo clippy --all-features -- -D warnings'
|
|
494
|
+
: stack.backend === 'rust-axum'
|
|
495
|
+
? 'cargo clippy'
|
|
496
|
+
: stack.backend === 'python-fastapi' || stack.structure === 'mcp-server'
|
|
497
|
+
? 'ruff check .'
|
|
498
|
+
: 'npx eslint src';
|
|
465
499
|
return JSON.stringify({
|
|
466
500
|
permissions: {
|
|
467
501
|
allow: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/utils/templates.ts"],"names":[],"mappings":"AAeA,MAAM,UAAU,mBAAmB,CAAC,MAAsB;IACxD,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,MAAM,CAAC;IAEpD,MAAM,YAAY,GAA2B;QAC3C,WAAW,EAAE;;;;;;+DAM8C;QAC3D,aAAa,EAAE;;;;;iCAKc;QAC7B,gBAAgB,EAAE;;;;;mCAKa;QAC/B,aAAa,EAAE;;;;;6CAK0B;KAC1C,CAAC;IAEF,MAAM,aAAa,GAA2B;QAC5C,KAAK,EAAE;;;;;uCAK4B;QACnC,GAAG,EAAE;;;;;sCAK6B;
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/utils/templates.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "{{PROJECT_NAME}}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
crate-type = ["cdylib", "rlib"]
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
leptos = { version = "0.7", features = ["csr"] }
|
|
11
|
+
console_error_panic_hook = "0.1"
|
|
12
|
+
wasm-bindgen = "0.2"
|
|
13
|
+
|
|
14
|
+
[profile.release]
|
|
15
|
+
lto = true
|
|
16
|
+
opt-level = "z"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>{{PROJECT_NAME}}</title>
|
|
7
|
+
<link data-trunk rel="scss" href="style/main.scss" />
|
|
8
|
+
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="z" />
|
|
9
|
+
</head>
|
|
10
|
+
<body></body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
use leptos::prelude::*;
|
|
2
|
+
|
|
3
|
+
pub fn main() {
|
|
4
|
+
console_error_panic_hook::set_once();
|
|
5
|
+
leptos::mount::mount_to_body(App);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
#[component]
|
|
9
|
+
pub fn App() -> impl IntoView {
|
|
10
|
+
let (count, set_count) = signal(0);
|
|
11
|
+
|
|
12
|
+
view! {
|
|
13
|
+
<main>
|
|
14
|
+
<h1>"{{PROJECT_NAME}}"</h1>
|
|
15
|
+
<button on:click=move |_| set_count.update(|n| *n += 1)>
|
|
16
|
+
"Count: " {count}
|
|
17
|
+
</button>
|
|
18
|
+
</main>
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "{{PROJECT_NAME}}"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
[lib]
|
|
7
|
+
crate-type = ["cdylib", "rlib"]
|
|
8
|
+
|
|
9
|
+
[dependencies]
|
|
10
|
+
axum = { version = "0.7", optional = true }
|
|
11
|
+
console_error_panic_hook = "0.1"
|
|
12
|
+
leptos = { version = "0.7", features = [] }
|
|
13
|
+
leptos_axum = { version = "0.7", optional = true }
|
|
14
|
+
leptos_meta = { version = "0.7" }
|
|
15
|
+
leptos_router = { version = "0.7", features = [] }
|
|
16
|
+
tokio = { version = "1", features = ["full"], optional = true }
|
|
17
|
+
tower = { version = "0.5", optional = true }
|
|
18
|
+
tower-http = { version = "0.6", features = ["fs"], optional = true }
|
|
19
|
+
wasm-bindgen = "0.2"
|
|
20
|
+
|
|
21
|
+
[features]
|
|
22
|
+
default = []
|
|
23
|
+
hydrate = ["leptos/hydrate"]
|
|
24
|
+
ssr = [
|
|
25
|
+
"dep:axum",
|
|
26
|
+
"dep:leptos_axum",
|
|
27
|
+
"dep:tokio",
|
|
28
|
+
"dep:tower",
|
|
29
|
+
"dep:tower-http",
|
|
30
|
+
"leptos/ssr",
|
|
31
|
+
"leptos_meta/ssr",
|
|
32
|
+
"leptos_router/ssr",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[package.metadata.leptos]
|
|
36
|
+
output-name = "{{PROJECT_NAME}}"
|
|
37
|
+
site-root = "target/site"
|
|
38
|
+
site-pkg-dir = "pkg"
|
|
39
|
+
style-file = "style/main.scss"
|
|
40
|
+
browserquery = "defaults"
|
|
41
|
+
env = "DEV"
|
|
42
|
+
bin-features = ["ssr"]
|
|
43
|
+
lib-features = ["hydrate"]
|
|
44
|
+
|
|
45
|
+
[[bin]]
|
|
46
|
+
name = "{{PROJECT_NAME}}"
|
|
47
|
+
path = "src/main.rs"
|
|
48
|
+
required-features = ["ssr"]
|
|
49
|
+
|
|
50
|
+
[profile.release]
|
|
51
|
+
codegen-units = 1
|
|
52
|
+
lto = true
|
|
53
|
+
|
|
54
|
+
[profile.wasm-release]
|
|
55
|
+
inherits = "release"
|
|
56
|
+
opt-level = "z"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
use leptos::prelude::*;
|
|
2
|
+
use leptos_meta::*;
|
|
3
|
+
use leptos_router::{components::Router, path};
|
|
4
|
+
|
|
5
|
+
pub fn shell(options: LeptosOptions) -> impl IntoView {
|
|
6
|
+
view! {
|
|
7
|
+
<!DOCTYPE html>
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="UTF-8"/>
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
12
|
+
<AutoReload options=options.clone()/>
|
|
13
|
+
<HydrationScripts options=options.clone()/>
|
|
14
|
+
<MetaTags/>
|
|
15
|
+
</head>
|
|
16
|
+
<body>
|
|
17
|
+
<App/>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
#[component]
|
|
24
|
+
pub fn App() -> impl IntoView {
|
|
25
|
+
provide_meta_context();
|
|
26
|
+
|
|
27
|
+
view! {
|
|
28
|
+
<Title text="{{PROJECT_NAME}}"/>
|
|
29
|
+
<Router>
|
|
30
|
+
<main>
|
|
31
|
+
<Routes fallback=|| view! { <p>"Not found."</p> }>
|
|
32
|
+
<Route path=path!("/") view=HomePage/>
|
|
33
|
+
</Routes>
|
|
34
|
+
</main>
|
|
35
|
+
</Router>
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[component]
|
|
40
|
+
fn HomePage() -> impl IntoView {
|
|
41
|
+
let (count, set_count) = signal(0);
|
|
42
|
+
|
|
43
|
+
view! {
|
|
44
|
+
<h1>"{{PROJECT_NAME}}"</h1>
|
|
45
|
+
<button on:click=move |_| set_count.update(|n| *n += 1)>
|
|
46
|
+
"Count: " {count}
|
|
47
|
+
</button>
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#[cfg(feature = "ssr")]
|
|
2
|
+
#[tokio::main]
|
|
3
|
+
async fn main() {
|
|
4
|
+
use axum::Router;
|
|
5
|
+
use leptos::prelude::*;
|
|
6
|
+
use leptos_axum::{generate_route_list, LeptosRoutes};
|
|
7
|
+
// Import from the lib target of this crate (same crate, lib target)
|
|
8
|
+
use app::{shell, App};
|
|
9
|
+
|
|
10
|
+
let conf = get_configuration(None).unwrap();
|
|
11
|
+
let leptos_options = conf.leptos_options;
|
|
12
|
+
let addr = leptos_options.site_addr;
|
|
13
|
+
let routes = generate_route_list(App);
|
|
14
|
+
|
|
15
|
+
let app = Router::new()
|
|
16
|
+
.leptos_routes(&leptos_options, routes, {
|
|
17
|
+
let leptos_options = leptos_options.clone();
|
|
18
|
+
move || shell(leptos_options.clone())
|
|
19
|
+
})
|
|
20
|
+
.fallback(leptos_axum::file_and_error_handler(shell))
|
|
21
|
+
.with_state(leptos_options);
|
|
22
|
+
|
|
23
|
+
println!("Listening on http://{}", addr);
|
|
24
|
+
let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
|
|
25
|
+
axum::serve(listener, app).await.unwrap();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[cfg(not(feature = "ssr"))]
|
|
29
|
+
pub fn main() {}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dare-rust-workspace
|
|
3
|
+
description: Decisão e migração de Cargo workspace multi-crate para projetos Rust/Axum. Use durante design/blueprint para decidir o layout, ou quando um projeto single-crate cresceu além do que comporta confortavelmente. Cobre os 2 cenários — escolher na fase de design + migrar projeto existente — com critérios objetivos e plano em PRs.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# DARE — Rust Workspace Skill (single-crate vs multi-crate)
|
|
7
|
+
|
|
8
|
+
Use esta skill em dois momentos:
|
|
9
|
+
|
|
10
|
+
- **Design / Blueprint:** projeto Rust novo na stack `rust-axum`. Decida
|
|
11
|
+
desde o início se ele nasce single-crate ou em workspace multi-crate.
|
|
12
|
+
- **Migração:** projeto Rust single-crate **já existente** que cresceu
|
|
13
|
+
demais e está doendo. Proponha o plano de migração.
|
|
14
|
+
|
|
15
|
+
Regra geral: **comece simples, migre quando os critérios objetivos
|
|
16
|
+
abaixo aparecerem.** Workspace é a estrutura idiomática Rust para projetos
|
|
17
|
+
maduros, mas é overengineering para o "hello world".
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Cenário A — Decisão na fase Design/Blueprint
|
|
22
|
+
|
|
23
|
+
### Comece **single-crate** quando TODOS forem verdadeiros
|
|
24
|
+
|
|
25
|
+
- Apenas **1 binário** (HTTP server).
|
|
26
|
+
- Estimativa de **< 30 arquivos `.rs`** no `src/`.
|
|
27
|
+
- **1–2 sistemas externos** (apenas DB; ou DB + cache).
|
|
28
|
+
- **Equipe ≤ 2 devs**.
|
|
29
|
+
- Nenhum requisito de deploy independente para subcomponentes.
|
|
30
|
+
|
|
31
|
+
### Comece **workspace multi-crate** quando QUALQUER um for verdadeiro
|
|
32
|
+
|
|
33
|
+
- **≥ 2 binários** previstos (API + worker; API + admin; API + CLI).
|
|
34
|
+
- **Múltiplos sistemas externos** (3+: PG + Redis + Rabbit + Qdrant + Neo4j…).
|
|
35
|
+
- **Deploy independente** desejado (workers em pods separados no k8s).
|
|
36
|
+
- **Fronteiras arquiteturais** críticas (domain puro sem HTTP/DB; SDK
|
|
37
|
+
publicável; cliente compartilhado com outros projetos).
|
|
38
|
+
- **Equipe ≥ 3 devs** trabalhando em paralelo.
|
|
39
|
+
|
|
40
|
+
### Layout convencional para workspace
|
|
41
|
+
|
|
42
|
+
Use o prefixo do projeto (`<p>`) — ex: `wa-` para `wa-business-api`,
|
|
43
|
+
`agent-` para `agent-ai`.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
projeto/
|
|
47
|
+
├── Cargo.toml # workspace root
|
|
48
|
+
├── docker-compose.yml
|
|
49
|
+
├── Dockerfile
|
|
50
|
+
├── crates/
|
|
51
|
+
│ ├── <p>-domain/ (lib) # entities puras
|
|
52
|
+
│ ├── <p>-config/ (lib) # AppConfig::from_env()
|
|
53
|
+
│ ├── <p>-db/ (lib) # sqlx pool, migrations
|
|
54
|
+
│ ├── <p>-cache/ (lib) # redis/moka
|
|
55
|
+
│ ├── <p>-queue/ (lib) # lapin/kafka
|
|
56
|
+
│ ├── <p>-crypto/ (lib) # bcrypt, jwt
|
|
57
|
+
│ ├── <p>-services/ (lib) # business logic, sem HTTP
|
|
58
|
+
│ ├── <p>-integrators/ (lib) # clientes externos (LLM, Stripe…)
|
|
59
|
+
│ ├── <p>-api/ (bin + lib) # HTTP server
|
|
60
|
+
│ ├── <p>-worker-<X>/ (bin) # 1 binário por tipo de worker
|
|
61
|
+
│ ├── <p>-admin/ (bin) # CLI admin
|
|
62
|
+
│ └── <p>-e2e/ (tests) # integration tests
|
|
63
|
+
└── xtask/ # scripts em Rust
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Granularidade: **um crate por bounded context**, NÃO por arquivo. Resista
|
|
67
|
+
ao "crate util" / "common" — vira lixeira.
|
|
68
|
+
|
|
69
|
+
### Cargo.toml workspace template
|
|
70
|
+
|
|
71
|
+
```toml
|
|
72
|
+
[workspace]
|
|
73
|
+
resolver = "2"
|
|
74
|
+
members = [
|
|
75
|
+
"crates/<p>-domain",
|
|
76
|
+
"crates/<p>-config",
|
|
77
|
+
"crates/<p>-db",
|
|
78
|
+
"crates/<p>-services",
|
|
79
|
+
"crates/<p>-api",
|
|
80
|
+
"crates/<p>-worker-x",
|
|
81
|
+
"crates/<p>-e2e",
|
|
82
|
+
"xtask",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[workspace.package]
|
|
86
|
+
version = "0.1.0"
|
|
87
|
+
edition = "2021"
|
|
88
|
+
rust-version = "1.80"
|
|
89
|
+
|
|
90
|
+
[workspace.dependencies]
|
|
91
|
+
tokio = { version = "1.40", features = ["full"] }
|
|
92
|
+
axum = { version = "0.7", features = ["macros"] }
|
|
93
|
+
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono", "migrate"] }
|
|
94
|
+
serde = { version = "1.0", features = ["derive"] }
|
|
95
|
+
serde_json = "1.0"
|
|
96
|
+
thiserror = "1.0"
|
|
97
|
+
anyhow = "1.0"
|
|
98
|
+
tracing = "0.1"
|
|
99
|
+
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
|
100
|
+
uuid = { version = "1.10", features = ["v4", "serde"] }
|
|
101
|
+
chrono = { version = "0.4", features = ["serde"] }
|
|
102
|
+
|
|
103
|
+
[profile.release]
|
|
104
|
+
opt-level = 3
|
|
105
|
+
lto = "thin"
|
|
106
|
+
codegen-units = 1
|
|
107
|
+
strip = true
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Cada crate filho herda com `version.workspace = true` e
|
|
111
|
+
`<dep> = { workspace = true }`. Atualizar versão de uma dep = uma linha
|
|
112
|
+
no root.
|
|
113
|
+
|
|
114
|
+
### Diagrama Mermaid no BLUEPRINT.md
|
|
115
|
+
|
|
116
|
+
```mermaid
|
|
117
|
+
graph TD
|
|
118
|
+
api[<p>-api]
|
|
119
|
+
admin[<p>-admin]
|
|
120
|
+
worker[<p>-worker-flow]
|
|
121
|
+
services[<p>-services]
|
|
122
|
+
integrators[<p>-integrators]
|
|
123
|
+
db[<p>-db]
|
|
124
|
+
queue[<p>-queue]
|
|
125
|
+
domain[<p>-domain]
|
|
126
|
+
|
|
127
|
+
api --> services
|
|
128
|
+
admin --> services
|
|
129
|
+
worker --> services
|
|
130
|
+
services --> db
|
|
131
|
+
services --> queue
|
|
132
|
+
services --> integrators
|
|
133
|
+
services --> domain
|
|
134
|
+
db --> domain
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Regras de seta:
|
|
138
|
+
- `<p>-domain` no fundo: ninguém depende para baixo dele.
|
|
139
|
+
- `<p>-services` no meio: depende de domain/db/queue, nunca de api.
|
|
140
|
+
- Binários (api/admin/worker) no topo: dependem de services, nunca um do
|
|
141
|
+
outro.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Cenário B — Migração de single-crate para workspace
|
|
146
|
+
|
|
147
|
+
### Sintomas objetivos de "hora de migrar"
|
|
148
|
+
|
|
149
|
+
- `src/` tem > 30 arquivos `.rs` ou > 6 subpastas top-level.
|
|
150
|
+
- Múltiplos "domínios verticais" misturados (handlers, services, workers,
|
|
151
|
+
integrators, mcp, acp, skills, tools…).
|
|
152
|
+
- Workers usam `tokio::spawn` no mesmo processo do API.
|
|
153
|
+
- `cargo build` incremental > 10s.
|
|
154
|
+
- Conflitos de merge frequentes no mesmo crate.
|
|
155
|
+
- Quer expor parte do código (cliente, SDK) como crate publicável.
|
|
156
|
+
|
|
157
|
+
### Plano em 4 PRs incrementais
|
|
158
|
+
|
|
159
|
+
Migração nunca é big-bang. Cada PR deve passar build/test/clippy no fim e
|
|
160
|
+
ser deployável.
|
|
161
|
+
|
|
162
|
+
#### PR 1 — Workers (mais isolados, menor risco)
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
src/workers/ → crates/<p>-worker-<nome>/
|
|
166
|
+
├── Cargo.toml
|
|
167
|
+
└── src/main.rs
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- Cada `tokio::spawn(worker_x)` do `main.rs` da API vira um binário
|
|
171
|
+
próprio.
|
|
172
|
+
- API server **para de spawn workers** — workers sobem como processo
|
|
173
|
+
independente.
|
|
174
|
+
- `docker-compose.yml` ganha service novo por worker.
|
|
175
|
+
- Em k8s, cada worker vira Deployment próprio com scaling independente.
|
|
176
|
+
|
|
177
|
+
#### PR 2 — Integrators (clientes externos)
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
src/integrators/{llm, neo4j, qdrant}.rs → crates/<p>-integrators/src/lib.rs
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- Tudo que fala com o mundo externo (LLM, DB externo, Stripe, Meta…) vira
|
|
184
|
+
lib.
|
|
185
|
+
- API e workers importam via `<p>-integrators = { path = "../<p>-integrators" }`.
|
|
186
|
+
- Crate é folha do grafo: NÃO depende de domain/services — só conhece
|
|
187
|
+
tipos request/response da API externa.
|
|
188
|
+
|
|
189
|
+
#### PR 3 — Domain (entidades puras)
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
src/models/ \
|
|
193
|
+
src/dto/ → crates/<p>-domain/src/lib.rs
|
|
194
|
+
src/error.rs (parte DomainError) /
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
- `<p>-domain` tem dependências **mínimas**: `serde`, `uuid`, `chrono`,
|
|
198
|
+
`thiserror`. Nada de axum, sqlx, redis.
|
|
199
|
+
- Force a regra: o Cargo.toml do domain NÃO inclui essas deps. Build
|
|
200
|
+
falha se alguém tentar.
|
|
201
|
+
- Todos os outros crates passam a usar `<p>-domain` em vez de
|
|
202
|
+
`crate::models::`.
|
|
203
|
+
|
|
204
|
+
#### PR 4 — API e raiz do workspace
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
Cargo.toml (raiz) → vira [workspace] puro, sem [package]
|
|
208
|
+
src/ (resto) → crates/<p>-api/src/
|
|
209
|
+
├── main.rs
|
|
210
|
+
├── lib.rs
|
|
211
|
+
├── handlers/
|
|
212
|
+
├── routes.rs
|
|
213
|
+
└── middleware/
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
- `Cargo.toml` raiz só tem `[workspace]` + `[workspace.package]` +
|
|
217
|
+
`[workspace.dependencies]` + `[profile.*]`.
|
|
218
|
+
- API vai pra `crates/<p>-api/`.
|
|
219
|
+
- Todas as deps centralizadas em `workspace.dependencies`.
|
|
220
|
+
|
|
221
|
+
### Validação após CADA PR
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
cargo build --workspace --all-targets
|
|
225
|
+
cargo test --workspace
|
|
226
|
+
cargo clippy --workspace --all-targets -- -D warnings
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Mais o smoke E2E (subir compose, hitar `/healthz`, login, operação CRUD).
|
|
230
|
+
Se algum quebrar, o PR está incompleto.
|
|
231
|
+
|
|
232
|
+
### Antipatterns ao migrar
|
|
233
|
+
|
|
234
|
+
| Antipattern | Por que evitar |
|
|
235
|
+
|-------------|----------------|
|
|
236
|
+
| Big-bang (1 PR mexe em tudo) | Impossível revisar; impossível reverter |
|
|
237
|
+
| Crate `common`/`shared`/`utils` | Vira lixeira; viola SRP |
|
|
238
|
+
| Crate por arquivo (granular demais) | 50 crates de 100 linhas → parar build |
|
|
239
|
+
| Refactor de lógica + migração no mesmo PR | Bug fica escondido |
|
|
240
|
+
| Mover testes em PR separado | Crate sem test em produção é bug em standby |
|
|
241
|
+
| Esquecer de atualizar `xtask`/CI | Pipeline quebra; deploy vira pesadelo |
|
|
242
|
+
|
|
243
|
+
### Tradução de imports
|
|
244
|
+
|
|
245
|
+
| Antes (single-crate) | Depois (workspace) |
|
|
246
|
+
|----------------------|--------------------|
|
|
247
|
+
| `use crate::models::User;` | `use <p>_domain::User;` |
|
|
248
|
+
| `use crate::services::auth::login;` | `use <p>_services::auth::login;` |
|
|
249
|
+
| `use crate::integrators::llm::Gemini;` | `use <p>_integrators::llm::Gemini;` |
|
|
250
|
+
| `use crate::handlers::health::router;` | *fica igual — mesmo crate* |
|
|
251
|
+
|
|
252
|
+
Hifens (Cargo) viram underlines (Rust idents).
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Quando NÃO migrar
|
|
257
|
+
|
|
258
|
+
- Projeto < 30 arquivos, 1 binário, 1 dev → single-crate é mais simples.
|
|
259
|
+
- Sprint crítico em curso → migração é refactor estrutural; não combina
|
|
260
|
+
com prazo.
|
|
261
|
+
- Sem sinais reais de dor → build < 5s, sem conflitos, sem segundo
|
|
262
|
+
binário planejado.
|
|
263
|
+
|
|
264
|
+
Migração tem custo. Faça quando o ganho compensar.
|
|
265
|
+
|
|
266
|
+
## Checklist final
|
|
267
|
+
|
|
268
|
+
- [ ] Critérios objetivos documentados no BLUEPRINT
|
|
269
|
+
- [ ] Lista de crates com responsabilidade de cada
|
|
270
|
+
- [ ] Diagrama Mermaid do grafo de dependências
|
|
271
|
+
- [ ] `Cargo.toml` raiz com `workspace.dependencies` centralizadas
|
|
272
|
+
- [ ] `<p>-domain` sem dependência de HTTP/DB/cache
|
|
273
|
+
- [ ] Cada binário com `[[bin]]` no Cargo.toml do seu crate
|
|
274
|
+
- [ ] Migração em PRs incrementais (não big-bang)
|
|
275
|
+
- [ ] Ralph Loop verde após cada PR (`cargo build/test/clippy --workspace`)
|