@ds9-solutions/mcp 0.1.0 → 0.3.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.
Files changed (70) hide show
  1. package/LICENSE +43 -0
  2. package/README.md +53 -0
  3. package/content/agents/code-reviewer.agent.md +52 -22
  4. package/content/agents/db-analyser.agent.md +121 -0
  5. package/content/agents/security-reviewer.agent.md +119 -37
  6. package/content/agents/test-writer.agent.md +84 -35
  7. package/content/skills/dart/SKILL.md +139 -0
  8. package/content/skills/dart/examples/entity.dart +37 -0
  9. package/content/skills/dart/examples/error_handling.dart +36 -0
  10. package/content/skills/dart/examples/order_test.dart +34 -0
  11. package/content/skills/dart/examples/repository.dart +57 -0
  12. package/content/skills/dart/references/checklist.md +12 -0
  13. package/content/skills/dart/references/documentation.md +26 -0
  14. package/content/skills/dart/references/naming.md +27 -0
  15. package/content/skills/dart-frog/SKILL.md +208 -0
  16. package/content/skills/dart-frog/examples/auth_middleware.dart +50 -0
  17. package/content/skills/dart-frog/examples/controller.dart +75 -0
  18. package/content/skills/dart-frog/examples/dto.dart +41 -0
  19. package/content/skills/dart-frog/examples/error_response.dart +35 -0
  20. package/content/skills/dart-frog/examples/exceptions.dart +60 -0
  21. package/content/skills/dart-frog/examples/middleware_composition.dart +34 -0
  22. package/content/skills/dart-frog/examples/model.dart +20 -0
  23. package/content/skills/dart-frog/examples/port.dart +17 -0
  24. package/content/skills/dart-frog/examples/repository.dart +56 -0
  25. package/content/skills/dart-frog/examples/route_collection.dart +25 -0
  26. package/content/skills/dart-frog/examples/route_item.dart +24 -0
  27. package/content/skills/dart-frog/examples/route_test.dart +33 -0
  28. package/content/skills/dart-frog/examples/service.dart +51 -0
  29. package/content/skills/dart-frog/examples/service_test.dart +55 -0
  30. package/content/skills/dart-frog/references/checklist.md +31 -0
  31. package/content/skills/dart-frog/references/error-handling.md +48 -0
  32. package/content/skills/dart-frog/references/fpdart-taskeither.md +53 -0
  33. package/content/skills/dart-frog/references/layers.md +43 -0
  34. package/content/skills/dart-frog/references/middlewares-di.md +45 -0
  35. package/content/skills/dart-frog/references/project-structure.md +67 -0
  36. package/content/skills/dart-frog/references/routing.md +53 -0
  37. package/content/skills/dart-frog/references/testing.md +24 -0
  38. package/content/skills/docker/SKILL.md +3 -3
  39. package/content/skills/driftorm/SKILL.md +249 -0
  40. package/content/skills/flutter/SKILL.md +419 -0
  41. package/content/skills/hex-pattern-dart/SKILL.md +83 -0
  42. package/content/skills/hex-pattern-dart/examples/adapter-in.md +24 -0
  43. package/content/skills/hex-pattern-dart/examples/adapter-out.md +20 -0
  44. package/content/skills/hex-pattern-dart/examples/errors.md +25 -0
  45. package/content/skills/hex-pattern-dart/examples/port.md +11 -0
  46. package/content/skills/hex-pattern-dart/examples/service.md +20 -0
  47. package/content/skills/hex-pattern-dart/examples/structure-tree.md +28 -0
  48. package/content/skills/hex-pattern-dart/references/anti-patterns.md +16 -0
  49. package/content/skills/hex-pattern-dart/references/checklist.md +34 -0
  50. package/content/skills/hex-pattern-dart/references/dependency-injection.md +32 -0
  51. package/content/skills/hex-pattern-dart/references/error-layers.md +27 -0
  52. package/content/skills/hex-pattern-dart/references/layers-and-dependency-rule.md +29 -0
  53. package/content/skills/hex-pattern-dart/references/persistence.md +23 -0
  54. package/content/skills/hex-pattern-dart/references/ports-and-adapters.md +33 -0
  55. package/content/skills/hex-pattern-dart/references/testing.md +30 -0
  56. package/content/skills/mysql/SKILL.md +3 -3
  57. package/content/skills/typescript/SKILL.md +4 -4
  58. package/dist/src/content.js +10 -2
  59. package/dist/src/init.js +1 -0
  60. package/dist/src/search.js +20 -12
  61. package/dist/src/server.js +60 -4
  62. package/dist/src/skill-files.js +125 -0
  63. package/package.json +2 -2
  64. package/content/agents/db-specialist.agent.md +0 -78
  65. package/content/agents/nestjs-api-designer.agent.md +0 -61
  66. package/content/skills/hexagonal/SKILL.md +0 -156
  67. package/content/skills/jest-testing/SKILL.md +0 -88
  68. package/content/skills/nestjs/SKILL.md +0 -100
  69. package/content/skills/typeorm/SKILL.md +0 -119
  70. package/dist/test/mcp.test.js +0 -112
@@ -0,0 +1,33 @@
1
+ // Caminho: test/routes/api/orders/index_test.dart
2
+ // Rota testada como função, com RequestContext mockado; sempre cobre o 405.
3
+ import 'dart:io';
4
+
5
+ import 'package:dart_frog/dart_frog.dart';
6
+ import 'package:mocktail/mocktail.dart';
7
+ import 'package:test/test.dart';
8
+
9
+ import '../../../../routes/api/orders/index.dart' as route;
10
+ import 'package:my_api/common/middleware/auth_middleware.dart';
11
+ import 'package:my_api/orders/adapter/controllers/order_controller.dart';
12
+
13
+ class _MockRequestContext extends Mock implements RequestContext {}
14
+
15
+ class _MockOrderController extends Mock implements OrderController {}
16
+
17
+ void main() {
18
+ late _MockRequestContext context;
19
+
20
+ setUp(() {
21
+ context = _MockRequestContext();
22
+ when(() => context.read<OrderController>()).thenReturn(_MockOrderController());
23
+ when(() => context.read<AuthenticatedUser>()).thenReturn(const AuthenticatedUser(id: 'u1'));
24
+ });
25
+
26
+ test('retorna 405 para método não suportado', () async {
27
+ when(() => context.request).thenReturn(Request.delete(Uri.parse('http://localhost/api/orders')));
28
+
29
+ final response = await route.onRequest(context);
30
+
31
+ expect(response.statusCode, equals(HttpStatus.methodNotAllowed));
32
+ });
33
+ }
@@ -0,0 +1,51 @@
1
+ // Caminho: lib/orders/domain/services/order_service.dart
2
+ import 'dart:io';
3
+
4
+ import 'package:fpdart/fpdart.dart';
5
+
6
+ import '../../../common/exception/exceptions.dart';
7
+ import '../models/order.dart';
8
+ import '../ports/order_repository_port.dart';
9
+
10
+ /// Regras de negócio de pedidos.
11
+ class OrderService {
12
+ /// Cria o serviço com o [_repository] injetado pelo middleware.
13
+ const OrderService(this._repository);
14
+
15
+ final OrderRepositoryPort _repository;
16
+
17
+ /// Busca o pedido [id] e garante que pertence a [requesterId].
18
+ TaskEither<BusinessException, Order> getOwnedById({
19
+ required String id,
20
+ required String requesterId,
21
+ }) {
22
+ return _repository.findById(id).flatMap((order) {
23
+ if (order == null) return TaskEither.left(_failure(HttpStatus.notFound, 'Pedido não encontrado.'));
24
+ if (order.ownerId != requesterId) {
25
+ return TaskEither.left(_failure(HttpStatus.forbidden, 'Acesso negado.'));
26
+ }
27
+ return TaskEither.right(order);
28
+ });
29
+ }
30
+
31
+ /// Cria um pedido para [ownerId]; rejeita [total] não positivo.
32
+ TaskEither<BusinessException, Order> create({
33
+ required String ownerId,
34
+ required double total,
35
+ }) {
36
+ if (total <= 0) {
37
+ return TaskEither.left(_failure(HttpStatus.badRequest, 'O total deve ser positivo.'));
38
+ }
39
+ return _repository.create(ownerId: ownerId, total: total);
40
+ }
41
+
42
+ BusinessException _failure(int statusCode, String message) {
43
+ return BusinessException(
44
+ domainName: 'order',
45
+ statusCode: statusCode,
46
+ message: message,
47
+ error: message,
48
+ stackTrace: StackTrace.current,
49
+ );
50
+ }
51
+ }
@@ -0,0 +1,55 @@
1
+ // Caminho: test/orders/domain/services/order_service_test.dart
2
+ // Service testado com fake do port, sem banco real.
3
+ import 'dart:io';
4
+
5
+ import 'package:fpdart/fpdart.dart';
6
+ import 'package:test/test.dart';
7
+
8
+ import 'package:my_api/common/exception/exceptions.dart';
9
+ import 'package:my_api/orders/domain/models/order.dart';
10
+ import 'package:my_api/orders/domain/ports/order_repository_port.dart';
11
+ import 'package:my_api/orders/domain/services/order_service.dart';
12
+
13
+ class _FakeRepository implements OrderRepositoryPort {
14
+ _FakeRepository(this.stored);
15
+
16
+ final Order? stored;
17
+
18
+ @override
19
+ TaskEither<BusinessException, Order?> findById(String id) => TaskEither.right(stored);
20
+
21
+ @override
22
+ TaskEither<BusinessException, Order> create({
23
+ required String ownerId,
24
+ required double total,
25
+ }) =>
26
+ TaskEither.right(Order(id: '1', ownerId: ownerId, total: total));
27
+ }
28
+
29
+ void main() {
30
+ group('OrderService.getOwnedById', () {
31
+ test('retorna Left 404 quando o pedido não existe', () async {
32
+ final service = OrderService(_FakeRepository(null));
33
+
34
+ final result = await service.getOwnedById(id: '1', requesterId: 'u1').run();
35
+
36
+ expect(result.getLeft().toNullable()?.statusCode, HttpStatus.notFound);
37
+ });
38
+
39
+ test('retorna Left 403 quando o pedido é de outro usuário', () async {
40
+ final service = OrderService(_FakeRepository(const Order(id: '1', ownerId: 'u2', total: 5)));
41
+
42
+ final result = await service.getOwnedById(id: '1', requesterId: 'u1').run();
43
+
44
+ expect(result.getLeft().toNullable()?.statusCode, HttpStatus.forbidden);
45
+ });
46
+
47
+ test('retorna Right quando o pedido pertence ao usuário', () async {
48
+ final service = OrderService(_FakeRepository(const Order(id: '1', ownerId: 'u1', total: 5)));
49
+
50
+ final result = await service.getOwnedById(id: '1', requesterId: 'u1').run();
51
+
52
+ expect(result.isRight(), isTrue);
53
+ });
54
+ });
55
+ }
@@ -0,0 +1,31 @@
1
+ # Dart Frog — Checklist de review detalhado
2
+
3
+ ## Pré-requisitos
4
+ - [ ] `dart_frog` e `fpdart` presentes no `pubspec.yaml` (senão, o usuário foi informado e autorizou a instalação).
5
+ - [ ] Estrutura existente preservada; padrão aplicado ao código novo.
6
+
7
+ ## Inegociáveis
8
+ - [ ] Contratos de port/service/repository/controller retornam `TaskEither`.
9
+ - [ ] Operações que podem lançar usam `TaskEither.tryCatch`.
10
+ - [ ] Toda rota termina o `switch` com `case _` → 405.
11
+ - [ ] Dependências compostas só em `_middleware.dart` (`provider`/`context.read`).
12
+ - [ ] `BusinessException`, `ClientException`, `MiddlewareException` nas camadas certas.
13
+ - [ ] Domínio agnóstico de persistência (só ports).
14
+
15
+ ## Camadas
16
+ - [ ] Rota fina; só a rota executa `.run()`.
17
+ - [ ] Controller não acessa persistência nem tem regra de negócio.
18
+ - [ ] Service sem HTTP, `RequestContext` ou SDK.
19
+ - [ ] Repository sem regra de negócio; falhas convertidas em `BusinessException`.
20
+ - [ ] Autorização de recurso no service, não no middleware.
21
+
22
+ ## Erros e segurança
23
+ - [ ] Sem stack trace, SQL, `error` original ou mensagem de SDK na resposta.
24
+ - [ ] Formato `{error:{code,message}}` único.
25
+ - [ ] Segredos no ambiente; nada sensível em logs.
26
+ - [ ] Papel/perfil do cliente nunca confiado sem validação no servidor.
27
+
28
+ ## Qualidade
29
+ - [ ] Skill `dart` seguida (`///` nas declarações públicas, nomenclatura, OOP).
30
+ - [ ] Arquivos gerados regenerados e não editados à mão.
31
+ - [ ] Testes: service, controller, rota (com 405).
@@ -0,0 +1,48 @@
1
+ # Dart Frog — Erros (referência)
2
+
3
+ Complementa a seção 5 do `SKILL.md`. Exemplos: `examples/exceptions.dart`, `error_response.dart`.
4
+
5
+ ## Camadas fixas
6
+
7
+ | Exceção | Quem lança (`Left`) | Campos |
8
+ |---|---|---|
9
+ | `BusinessException` | service, repository | `domainName`, `statusCode`, `message`, `error`, `stackTrace` |
10
+ | `ClientException` | controller | `statusCode`, `message` |
11
+ | `MiddlewareException` | middleware | `statusCode`, `message`, `stackTrace?` |
12
+
13
+ - `message` é **sempre** segura para o cliente. `error` e `stackTrace` são só para log.
14
+ - Ao converter entre camadas, preserve o `statusCode`. Para `5xx`, a mensagem ao cliente é genérica.
15
+ - Não use `Exception` genérica como falha de `TaskEither`.
16
+
17
+ ## Status HTTP
18
+
19
+ | Situação | Status |
20
+ |---|---|
21
+ | Entrada inválida, body malformado | 400 |
22
+ | Não autenticado ou credencial inválida | 401 |
23
+ | Autenticado sem permissão (ex.: recurso de outro usuário) | 403 |
24
+ | Recurso inexistente | 404 |
25
+ | Método não suportado | 405 |
26
+ | Conflito de estado | 409 |
27
+ | Falha interna ou de persistência | 500 |
28
+ | Criação com sucesso | 201 |
29
+
30
+ ## Formato único
31
+
32
+ ```json
33
+ { "error": { "code": "not_found", "message": "Pedido não encontrado." } }
34
+ ```
35
+
36
+ ## Nunca
37
+
38
+ - Enviar stack trace, SQL, causa original (`error`), caminhos, credenciais ou mensagens de SDK.
39
+ - Devolver `f.error.toString()` ao cliente.
40
+ - Capturar erro e responder sucesso vazio.
41
+ - Logar tokens, documentos, e-mail, telefone ou conteúdo sensível completo.
42
+
43
+ ## Onde cada falha nasce
44
+
45
+ - Formato de body/query/params: controller/DTO (`400`).
46
+ - Regra de negócio e autorização de recurso: service (`403`/`404`/`409`).
47
+ - Persistência ou integração: repository (`500`, com `error` original preservado).
48
+ - Autenticação e rate limit: middleware (`401`/`429`).
@@ -0,0 +1,53 @@
1
+ # Dart Frog — `fpdart` e `TaskEither` (referência)
2
+
3
+ Complementa a seção 4 do `SKILL.md`. Exemplos: `examples/repository.dart`, `service.dart`, `controller.dart`.
4
+
5
+ ## Pré-requisito: `fpdart` instalado
6
+
7
+ 1. Procure `fpdart` em `dependencies` no `pubspec.yaml`.
8
+ 2. Se não estiver, **informe que ele é obrigatório para seguir o padrão DS9** e peça autorização para instalar: `dart pub add fpdart`.
9
+ 3. Não gere código fora do padrão para contornar a ausência.
10
+
11
+ ## Regras
12
+
13
+ - Todo contrato de camada que pode falhar retorna `TaskEither<Falha, T>`: `BusinessException` (port/service/repository), `ClientException` (controller).
14
+ - Operação assíncrona que pode lançar vai dentro de `TaskEither.tryCatch(run, onError)`; `onError` converte a causa (`error`, `stackTrace`) na exceção da camada, sem perder a causa original.
15
+ - Composição com `map`, `flatMap` e `mapLeft`. Regra violada: `TaskEither.left(...)`; sucesso: `TaskEither.right(...)`.
16
+ - **Só a rota executa** (`.run()`), e usa `match` para produzir a `Response`.
17
+ - Funções puras e sem I/O (validações locais, construtores, `toJson`) podem retornar valores comuns, ou `Either` quando houver falha de validação.
18
+ - Proibido: `throw` que escape da camada, `catch (_)` amplo, `Exception` genérica como falha, falha convertida em sucesso vazio.
19
+
20
+ ## Padrões
21
+
22
+ ```dart
23
+ // 1) Acesso externo (repository)
24
+ TaskEither<BusinessException, Order?> findById(String id) => TaskEither.tryCatch(
25
+ () => _dataSource.read(id),
26
+ (error, stackTrace) => BusinessException(
27
+ domainName: 'order',
28
+ statusCode: HttpStatus.internalServerError,
29
+ message: 'Falha ao consultar o pedido.',
30
+ error: error,
31
+ stackTrace: stackTrace,
32
+ ),
33
+ );
34
+
35
+ // 2) Regra de negócio (service)
36
+ _repository.findById(id).flatMap(
37
+ (order) => order == null ? TaskEither.left(_notFound(id)) : TaskEither.right(order),
38
+ );
39
+
40
+ // 3) Troca de camada de erro (controller)
41
+ _service.getOwnedById(id: id, requesterId: userId).mapLeft(_toClient).map(OrderResponse.fromDomain);
42
+
43
+ // 4) Execução (rota)
44
+ final result = await controller.getById(id, user.id).run();
45
+ return result.match(ErrorResponse.fromClient, (order) => Response.json(body: order.toJson()));
46
+ ```
47
+
48
+ ## Cuidados
49
+
50
+ - Mantenha as cadeias legíveis: se passar de 3 ou 4 etapas, extraia métodos privados nomeados.
51
+ - Não misture `Future` simples e `TaskEither` no mesmo contrato.
52
+ - Teste `Left` e `Right` explicitamente (`result.isLeft()`, `result.getLeft()`, `result.getRight()`).
53
+ - Esta é a exceção funcional aceita pela skill `dart`: vale só para os contratos entre camadas. O restante continua orientado a objetos.
@@ -0,0 +1,43 @@
1
+ # Dart Frog — Responsabilidade por camada (referência)
2
+
3
+ Complementa a seção 3 do `SKILL.md`. Exemplos: `examples/`.
4
+
5
+ ## Route (`routes/**`)
6
+
7
+ Faz: `switch` no método HTTP, `context.read<Controller>()`, `.run()` do `TaskEither`, converte `Left`/`Right` em `Response`, `case _` com 405.
8
+ Não faz: regra de negócio, query, SDK, instanciar dependência, ler body (isso é do controller).
9
+
10
+ ## Controller (`adapter/controllers`)
11
+
12
+ Faz: lê body/query/path/providers do `RequestContext` recebido **por chamada**, converte para DTO e valida formato, chama o service, converte modelo → DTO, converte `BusinessException` em `ClientException`.
13
+ Retorna `TaskEither<ClientException, T>`. Não acessa banco, não tem regra de negócio, não monta `Response`.
14
+ O controller é um objeto **provido por middleware** com o service no construtor; o `RequestContext` entra como parâmetro dos métodos.
15
+
16
+ ## Service (`domain/services`)
17
+
18
+ Faz: regras de negócio, autorização de domínio (ex.: o pedido pertence ao usuário), composição de ports com `flatMap`/`map`.
19
+ Retorna `TaskEither<BusinessException, T>`. Recebe ports no construtor (`final`), não conhece HTTP, headers, `RequestContext`, SDK ou persistência, não instancia dependências.
20
+
21
+ ## Port (`domain/ports`)
22
+
23
+ `abstract interface class` pequeno e focado. Assinaturas só com modelos de domínio e tipos simples (nunca DTO, `RequestContext`, `Response`, tipos de ORM ou SDK). Retorna `TaskEither<BusinessException, T>`.
24
+
25
+ ## Repository (`adapter/repository`)
26
+
27
+ Implementa o port, acessa a persistência (via adapter/fonte de dados), mapeia linha → modelo, envolve cada acesso em `TaskEither.tryCatch` convertendo a causa em `BusinessException`. Sem regra de negócio ou de autorização. A tecnologia concreta é decidida pela skill de persistência do projeto.
28
+
29
+ ## DTO (`adapter/dtos`)
30
+
31
+ Entrada: `fromJson` + `validate()` (formato). Saída: `fromDomain` + `toJson()`. Nome indica a operação (`CreateOrderRequest`, `OrderResponse`).
32
+
33
+ ## Model (`domain/models`)
34
+
35
+ Classes imutáveis (`final`, `const`), sem conhecer DTO ou persistência. Invariantes validadas na criação quando possível.
36
+
37
+ ## Middleware
38
+
39
+ Autentica, compõe providers, encerra com `MiddlewareException`. Não substitui a cadeia controller → service → repository nem contém regra de negócio.
40
+
41
+ ## Comunicação entre contextos
42
+
43
+ Um contexto consome outro via port ou service público, nunca via repository concreto; sem dependência circular.
@@ -0,0 +1,45 @@
1
+ # Dart Frog — Middlewares e injeção de dependência (referência)
2
+
3
+ Complementa a seção 7 do `SKILL.md`. Exemplos: `examples/middleware_composition.dart`, `auth_middleware.dart`.
4
+
5
+ ## Regra
6
+
7
+ A composição de dependências acontece **somente** em `routes/**/_middleware.dart`, com `provider<T>`; a leitura é `context.read<T>()`. Nunca instancie service, repository ou controller no handler, nem use service locator.
8
+
9
+ ## Um `_middleware.dart` por diretório
10
+
11
+ Exporta `Handler middleware(Handler handler)` e vale para a pasta e subpastas. Encadeie com `handler.use(...)`.
12
+
13
+ ## Ordem de resolução
14
+
15
+ A resolução é de baixo para cima: se `B` depende de `A`, declare `.use(B).use(A)`. Exemplo: controller → service → repository.
16
+
17
+ ## Cache
18
+
19
+ `provider` é lazy e recria o valor a cada leitura. Para instâncias compartilhadas ou caras, faça cache em variável privada do arquivo: `_service ??= OrderService(...)`.
20
+
21
+ ## Distribuição por nível
22
+
23
+ | Middleware | Provê |
24
+ |---|---|
25
+ | `routes/_middleware.dart` | CORS, infraestrutura compartilhada (conexão, configuração, rate limit) |
26
+ | `routes/api/_middleware.dart` | autenticação, usuário autenticado |
27
+ | `routes/admin/_middleware.dart` | autenticação + verificação de papel administrativo |
28
+ | `routes/<grupo>/<recurso>/_middleware.dart` | controller/service/repository do recurso |
29
+
30
+ Cada middleware específico provê só o necessário ao seu ramo.
31
+
32
+ ## Autenticação
33
+
34
+ Middleware valida credenciais por um **port** (ex.: `TokenVerifierPort`), provê o usuário e encerra com `MiddlewareException` (`401`/`403`) quando inválido. Credenciais e chaves vêm do ambiente, nunca do código.
35
+
36
+ ## Proibido
37
+
38
+ - Regra de negócio em middleware (inclusive verificar dono do recurso): isso é do service.
39
+ - Ler parâmetro de caminho por posição (`pathSegments[n]`) em middleware: frágil e acoplado à URL. Receba o `id` na rota e valide no service.
40
+ - `context.read` em models, ports, services e repositories.
41
+ - Segundo ponto de composição fora de `_middleware.dart`.
42
+
43
+ ## Variáveis de ambiente
44
+
45
+ Leia `Platform.environment` em um ponto único (`common/environment`) e proveja a configuração. `String.fromEnvironment` não funciona no Dart Frog.
@@ -0,0 +1,67 @@
1
+ # Dart Frog — Estrutura de projeto (referência)
2
+
3
+ Complementa a seção 2 do `SKILL.md`.
4
+
5
+ ## Árvore completa
6
+
7
+ ```text
8
+ <projeto>/
9
+ ├── routes/
10
+ │ ├── _middleware.dart # raiz: CORS, infraestrutura compartilhada, rate limit
11
+ │ ├── index.dart # GET /
12
+ │ ├── public/ # sem autenticação
13
+ │ │ └── _middleware.dart
14
+ │ ├── api/ # usuários autenticados
15
+ │ │ ├── _middleware.dart # autenticação (provê o usuário)
16
+ │ │ └── orders/
17
+ │ │ ├── _middleware.dart # composição do contexto
18
+ │ │ ├── index.dart # /api/orders
19
+ │ │ └── [id].dart # /api/orders/:id
20
+ │ └── admin/ # operações administrativas
21
+ │ └── _middleware.dart # autenticação + papel administrativo
22
+ ├── lib/
23
+ │ ├── orders/ # um contexto de negócio
24
+ │ │ ├── domain/
25
+ │ │ │ ├── models/ # entidades e valores
26
+ │ │ │ ├── ports/ # contratos (abstract interface class)
27
+ │ │ │ └── services/ # regras de negócio
28
+ │ │ └── adapter/
29
+ │ │ ├── controllers/ # HTTP -> domínio
30
+ │ │ ├── dtos/ # entrada e saída da API
31
+ │ │ └── repository/ # domínio -> persistência
32
+ │ └── common/
33
+ │ ├── exception/ # BusinessException, ClientException, MiddlewareException
34
+ │ ├── http/ # formato de erro, helpers de resposta
35
+ │ ├── middleware/ # auth, CORS, rate limit reutilizáveis
36
+ │ ├── environment/ # leitura única de variáveis de ambiente
37
+ │ ├── logger/
38
+ │ └── models/ # paginação e tipos compartilhados
39
+ ├── test/ # espelha lib/ e routes/
40
+ ├── main.dart # só init() e run()
41
+ ├── pubspec.yaml
42
+ └── analysis_options.yaml
43
+ ```
44
+
45
+ ## Variações permitidas
46
+
47
+ - `application/use_case/` dentro de um contexto, **só** quando houver orquestração entre vários services/ports. Não crie por antecipação.
48
+ - `adapter/<integração>/` (e-mail, mensageria, storage) para SDKs externos, sempre atrás de um port.
49
+ - Infraestrutura de persistência compartilhada em `common/infra/` (detalhes na skill de persistência do projeto).
50
+ - Não crie pasta `mappers/` por antecipação: o mapeamento fica no repository (linha → modelo) e no controller/DTO (modelo → DTO).
51
+
52
+ ## Nomenclatura de arquivos
53
+
54
+ `snake_case.dart` com sufixo do papel: `_model`, `_dto`, `_service`, `_service_port`, `_repository`, `_repository_port`, `_controller`, `_middleware`, `_use_case`. Demais regras: skill `dart`.
55
+
56
+ ## Grupos de rota
57
+
58
+ | Grupo | Pasta | Middleware |
59
+ |---|---|---|
60
+ | Base | `routes/index.dart` | raiz |
61
+ | Pública | `routes/public/**` | rate limit, sem autenticação |
62
+ | Autenticada | `routes/api/**` | autenticação, provê o usuário |
63
+ | Administrativa | `routes/admin/**` | autenticação + papel administrativo |
64
+
65
+ ## Imports
66
+
67
+ Dentro de `lib/`, use imports relativos (skill `dart`). Em `routes/`, `main.dart` e `test/` use `package:<nome_do_pacote>/...`.
@@ -0,0 +1,53 @@
1
+ # Dart Frog — Rotas (referência)
2
+
3
+ Complementa a seção 6 do `SKILL.md`. Exemplos: `examples/route_collection.dart`, `route_item.dart`.
4
+
5
+ ## Convenções do roteador
6
+
7
+ - Uma rota = um arquivo em `routes/` que exporta `onRequest(RequestContext context, ...)`. `index.dart` mapeia para a pasta.
8
+ - Rota dinâmica: `[id].dart` ou pasta `[id]/`; cada parâmetro vira argumento `String` posicional, na ordem do caminho.
9
+ - Wildcard `[...rest].dart` deve ser o único arquivo da pasta.
10
+ - Não coexistam `routes/api.dart` e `routes/api/index.dart` (conflito no build).
11
+ - Handler assíncrono retorna `Future<Response>`.
12
+
13
+ ## 405 obrigatório
14
+
15
+ Todo handler despacha por método e termina com o caso padrão:
16
+
17
+ ```dart
18
+ switch (context.request.method) {
19
+ case HttpMethod.get:
20
+ // ...
21
+ case _:
22
+ return Response(statusCode: HttpStatus.methodNotAllowed);
23
+ }
24
+ ```
25
+
26
+ Rotas inexistentes (caminho não encontrado) são tratadas pelo próprio Dart Frog com 404; o 405 cobre método não suportado em uma rota existente.
27
+
28
+ ## Request e Response
29
+
30
+ - O body só pode ser lido uma vez (`body()`, `json()` ou `formData()`); a leitura é do controller.
31
+ - `json()` devolve `dynamic`: valide e converta para DTO tipado; payload inválido responde 400.
32
+ - `formData()` lança `StateError` se o content-type não for de formulário: verifique antes.
33
+ - Sucesso: `Response.json(body: ...)`; criação: `201`; use `HttpStatus` em vez de números.
34
+ - Listagens mantêm o formato adotado pelo endpoint (lista direta ou objeto com dados e metadados).
35
+
36
+ ## Paginação
37
+
38
+ Parâmetros `page` e `pageSize` na query, lidos no controller; o repository devolve itens, total, página e tamanho; o service só propaga. Defina um tipo compartilhado em `common/models` e mantenha o padrão em todos os endpoints.
39
+
40
+ ## CLI
41
+
42
+ - `dart pub global activate dart_frog_cli` instala a CLI.
43
+ - `dart_frog create <nome>`, `dart_frog dev` (hot reload; `--port`), `dart_frog build` (build de produção em `build/`, com Dockerfile).
44
+ - `dart_frog new route "/caminho"` e `dart_frog new middleware "/caminho"`: prefira a CLI a criar à mão.
45
+ - Versões da CLI e do pacote `dart_frog` devem ser compatíveis.
46
+
47
+ ## Estáticos
48
+
49
+ Arquivos estáticos ficam em `public/` na raiz; não repita nomes existentes em `routes/`.
50
+
51
+ ## Ciclo de vida
52
+
53
+ `main.dart` define só `init()` (execução única no start) e `run()` (entrypoint customizado; roda a cada hot reload). Não compõe services.
@@ -0,0 +1,24 @@
1
+ # Dart Frog — Testes (referência)
2
+
3
+ Complementa a seção 9 do `SKILL.md`. Exemplos: `examples/service_test.dart`, `route_test.dart`. Para criar e executar, use o agente `test-writer`.
4
+
5
+ ## Estrutura
6
+
7
+ `test/` espelha `lib/` e `routes/` (ex.: `test/orders/domain/services/order_service_test.dart`, `test/routes/api/orders/index_test.dart`). Ferramentas: `package:test` e `package:mocktail`.
8
+
9
+ ## O que testar
10
+
11
+ | Alvo | Como | Cobrir |
12
+ |---|---|---|
13
+ | Service | fake/mock do port, sem banco real | cada regra de negócio, `Left` com status correto e `Right` |
14
+ | Controller | service mockado e `RequestContext` mockado | entrada válida, body inválido, validação, `Left`→`ClientException`, `Right` |
15
+ | Repository | integração separada, quando houver persistência real | mapeamento e conversão de falha em `BusinessException` |
16
+ | Rota | `RequestContext` mockado, handler como função | cada método suportado **e o 405** |
17
+ | Middleware | `Handler` interno stubado | provê o valor; encerra com 401/403 |
18
+
19
+ ## Dicas
20
+
21
+ - Mocke o `RequestContext` e faça stub de `context.request` (com `Request.get/post/delete` reais) e de `context.read<T>()`.
22
+ - Verifique `Either` com `result.isLeft()`, `result.getLeft()`, `result.getRight()`.
23
+ - Sem serviços externos reais em testes unitários.
24
+ - Toda regra de negócio nova tem teste no service.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: docker
3
- description: Diretrizes de containerização Docker para projetos NestJS/Node.js da DS9 — Dockerfile multi-stage, docker-compose para ambiente local, segurança de imagem e variáveis de ambiente. Use ao criar/revisar `Dockerfile`, `docker-compose.yml`, ou quando o usuário pedir "containerizar", "imagem Docker" ou "subir ambiente local".
3
+ description: Diretrizes de containerização Docker para projetos Node.js da DS9 — Dockerfile multi-stage, docker-compose para ambiente local, segurança de imagem e variáveis de ambiente. Use ao criar/revisar `Dockerfile`, `docker-compose.yml`, ou quando o usuário pedir "containerizar", "imagem Docker" ou "subir ambiente local".
4
4
  ---
5
5
 
6
6
  # Docker — Diretrizes DS9
7
7
 
8
- Convenções para containerizar aplicações NestJS/Node.js e seus serviços de apoio (MySQL, etc.) nos projetos da empresa.
8
+ Convenções para containerizar aplicações Node.js e seus serviços de apoio (MySQL, etc.) nos projetos da empresa.
9
9
 
10
10
  ## Dockerfile multi-stage (obrigatório)
11
11
 
@@ -42,7 +42,7 @@ Regras obrigatórias:
42
42
 
43
43
  ## Variáveis de ambiente e segredos
44
44
 
45
- - Nenhum segredo (senha de banco, JWT secret, API key) é copiado para dentro da imagem ou escrito em `ENV` no Dockerfile. Segredos entram em runtime via variáveis de ambiente/secret manager do orquestrador (ver skill `nestjs` para `ConfigService`).
45
+ - Nenhum segredo (senha de banco, JWT secret, API key) é copiado para dentro da imagem ou escrito em `ENV` no Dockerfile. Segredos entram em runtime via variáveis de ambiente/secret manager do orquestrador (use o mecanismo de configuração do projeto).
46
46
  - `.env` usado em desenvolvimento local nunca é commitado (deve estar no `.gitignore`) nem copiado para a imagem (`.dockerignore`).
47
47
  - `docker-compose.yml` de desenvolvimento pode referenciar um `.env` local via `env_file`, mas o arquivo de exemplo versionado é `.env.example` sem valores reais.
48
48