@dewtech/dare-cli 3.11.0 → 3.13.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 +2 -0
- package/dist/__tests__/cli-only-invariants.test.d.ts +2 -0
- package/dist/__tests__/cli-only-invariants.test.d.ts.map +1 -0
- package/dist/__tests__/cli-only-invariants.test.js +100 -0
- package/dist/__tests__/cli-only-invariants.test.js.map +1 -0
- package/dist/__tests__/cli-only-regression.test.d.ts +2 -0
- package/dist/__tests__/cli-only-regression.test.d.ts.map +1 -0
- package/dist/__tests__/cli-only-regression.test.js +29 -0
- package/dist/__tests__/cli-only-regression.test.js.map +1 -0
- package/dist/__tests__/ensure-skills.test.js +5 -0
- package/dist/__tests__/ensure-skills.test.js.map +1 -1
- package/dist/__tests__/ide-command-parity.test.js +1 -0
- package/dist/__tests__/ide-command-parity.test.js.map +1 -1
- package/dist/__tests__/project-generator.test.js +17 -0
- package/dist/__tests__/project-generator.test.js.map +1 -1
- package/dist/__tests__/reverse-facts.test.js +1 -0
- package/dist/__tests__/reverse-facts.test.js.map +1 -1
- package/dist/__tests__/terminal-parity-regression.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity-regression.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity-regression.test.js +116 -0
- package/dist/__tests__/terminal-parity-regression.test.js.map +1 -0
- package/dist/__tests__/terminal-parity.test.d.ts +2 -0
- package/dist/__tests__/terminal-parity.test.d.ts.map +1 -0
- package/dist/__tests__/terminal-parity.test.js +81 -0
- package/dist/__tests__/terminal-parity.test.js.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/antigravity-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/antigravity-driver.test.js +52 -0
- package/dist/agent/__tests__/antigravity-driver.test.js.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/codex-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/codex-driver.test.js +68 -0
- package/dist/agent/__tests__/codex-driver.test.js.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts +2 -0
- package/dist/agent/__tests__/cursor-driver.test.d.ts.map +1 -0
- package/dist/agent/__tests__/cursor-driver.test.js +52 -0
- package/dist/agent/__tests__/cursor-driver.test.js.map +1 -0
- package/dist/agent/driver.d.ts +1 -1
- package/dist/agent/driver.d.ts.map +1 -1
- package/dist/agent/drivers/antigravity.d.ts +8 -0
- package/dist/agent/drivers/antigravity.d.ts.map +1 -0
- package/dist/agent/drivers/antigravity.js +99 -0
- package/dist/agent/drivers/antigravity.js.map +1 -0
- package/dist/agent/drivers/codex.d.ts +12 -0
- package/dist/agent/drivers/codex.d.ts.map +1 -0
- package/dist/agent/drivers/codex.js +137 -0
- package/dist/agent/drivers/codex.js.map +1 -0
- package/dist/agent/drivers/cursor.d.ts +8 -0
- package/dist/agent/drivers/cursor.d.ts.map +1 -0
- package/dist/agent/drivers/cursor.js +99 -0
- package/dist/agent/drivers/cursor.js.map +1 -0
- package/dist/ai/__tests__/ai-core.test.d.ts +2 -0
- package/dist/ai/__tests__/ai-core.test.d.ts.map +1 -0
- package/dist/ai/__tests__/ai-core.test.js +41 -0
- package/dist/ai/__tests__/ai-core.test.js.map +1 -0
- package/dist/ai/__tests__/parity.test.d.ts +2 -0
- package/dist/ai/__tests__/parity.test.d.ts.map +1 -0
- package/dist/ai/__tests__/parity.test.js +36 -0
- package/dist/ai/__tests__/parity.test.js.map +1 -0
- package/dist/ai/__tests__/pipeline.test.d.ts +2 -0
- package/dist/ai/__tests__/pipeline.test.d.ts.map +1 -0
- package/dist/ai/__tests__/pipeline.test.js +147 -0
- package/dist/ai/__tests__/pipeline.test.js.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts +2 -0
- package/dist/ai/__tests__/refine-bridge.test.d.ts.map +1 -0
- package/dist/ai/__tests__/refine-bridge.test.js +17 -0
- package/dist/ai/__tests__/refine-bridge.test.js.map +1 -0
- package/dist/ai/__tests__/resolve.test.d.ts +2 -0
- package/dist/ai/__tests__/resolve.test.d.ts.map +1 -0
- package/dist/ai/__tests__/resolve.test.js +42 -0
- package/dist/ai/__tests__/resolve.test.js.map +1 -0
- package/dist/ai/capabilities.d.ts +3 -0
- package/dist/ai/capabilities.d.ts.map +1 -0
- package/dist/ai/capabilities.js +11 -0
- package/dist/ai/capabilities.js.map +1 -0
- package/dist/ai/command-options.d.ts +10 -0
- package/dist/ai/command-options.d.ts.map +1 -0
- package/dist/ai/command-options.js +15 -0
- package/dist/ai/command-options.js.map +1 -0
- package/dist/ai/config.d.ts +27 -0
- package/dist/ai/config.d.ts.map +1 -0
- package/dist/ai/config.js +89 -0
- package/dist/ai/config.js.map +1 -0
- package/dist/ai/parity.d.ts +13 -0
- package/dist/ai/parity.d.ts.map +1 -0
- package/dist/ai/parity.js +87 -0
- package/dist/ai/parity.js.map +1 -0
- package/dist/ai/parse-json-output.d.ts +5 -0
- package/dist/ai/parse-json-output.d.ts.map +1 -0
- package/dist/ai/parse-json-output.js +25 -0
- package/dist/ai/parse-json-output.js.map +1 -0
- package/dist/ai/pipeline.d.ts +20 -0
- package/dist/ai/pipeline.d.ts.map +1 -0
- package/dist/ai/pipeline.js +303 -0
- package/dist/ai/pipeline.js.map +1 -0
- package/dist/ai/prompts.d.ts +6 -0
- package/dist/ai/prompts.d.ts.map +1 -0
- package/dist/ai/prompts.js +49 -0
- package/dist/ai/prompts.js.map +1 -0
- package/dist/ai/providers.d.ts +63 -0
- package/dist/ai/providers.d.ts.map +1 -0
- package/dist/ai/providers.js +297 -0
- package/dist/ai/providers.js.map +1 -0
- package/dist/ai/refine-bridge.d.ts +5 -0
- package/dist/ai/refine-bridge.d.ts.map +1 -0
- package/dist/ai/refine-bridge.js +14 -0
- package/dist/ai/refine-bridge.js.map +1 -0
- package/dist/ai/registry.d.ts +12 -0
- package/dist/ai/registry.d.ts.map +1 -0
- package/dist/ai/registry.js +43 -0
- package/dist/ai/registry.js.map +1 -0
- package/dist/ai/resolve.d.ts +28 -0
- package/dist/ai/resolve.d.ts.map +1 -0
- package/dist/ai/resolve.js +83 -0
- package/dist/ai/resolve.js.map +1 -0
- package/dist/ai/schemas.d.ts +175 -0
- package/dist/ai/schemas.d.ts.map +1 -0
- package/dist/ai/schemas.js +199 -0
- package/dist/ai/schemas.js.map +1 -0
- package/dist/ai/types.d.ts +52 -0
- package/dist/ai/types.d.ts.map +1 -0
- package/dist/ai/types.js +8 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/bin/dare.js +2 -0
- package/dist/bin/dare.js.map +1 -1
- package/dist/commands/__tests__/ai-command.test.d.ts +2 -0
- package/dist/commands/__tests__/ai-command.test.d.ts.map +1 -0
- package/dist/commands/__tests__/ai-command.test.js +68 -0
- package/dist/commands/__tests__/ai-command.test.js.map +1 -0
- package/dist/commands/__tests__/execute-agent.test.js +82 -0
- package/dist/commands/__tests__/execute-agent.test.js.map +1 -1
- package/dist/commands/ai.d.ts +3 -0
- package/dist/commands/ai.d.ts.map +1 -0
- package/dist/commands/ai.js +141 -0
- package/dist/commands/ai.js.map +1 -0
- package/dist/commands/blueprint.d.ts.map +1 -1
- package/dist/commands/blueprint.js +17 -3
- package/dist/commands/blueprint.js.map +1 -1
- package/dist/commands/design.d.ts.map +1 -1
- package/dist/commands/design.js +21 -2
- package/dist/commands/design.js.map +1 -1
- package/dist/commands/discover.d.ts.map +1 -1
- package/dist/commands/discover.js +9 -1
- package/dist/commands/discover.js.map +1 -1
- package/dist/commands/dna.d.ts.map +1 -1
- package/dist/commands/dna.js +23 -3
- package/dist/commands/dna.js.map +1 -1
- package/dist/commands/execute.d.ts +11 -0
- package/dist/commands/execute.d.ts.map +1 -1
- package/dist/commands/execute.js +111 -4
- package/dist/commands/execute.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +14 -2
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/patterns.d.ts.map +1 -1
- package/dist/commands/patterns.js +14 -2
- package/dist/commands/patterns.js.map +1 -1
- package/dist/commands/refine.d.ts.map +1 -1
- package/dist/commands/refine.js +23 -2
- package/dist/commands/refine.js.map +1 -1
- package/dist/commands/reverse.d.ts.map +1 -1
- package/dist/commands/reverse.js +28 -3
- package/dist/commands/reverse.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +25 -3
- package/dist/commands/review.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/run_dag.d.ts +1 -1
- package/dist/dag-runner/run_dag.d.ts.map +1 -1
- package/dist/exec/safe-spawn.d.ts.map +1 -1
- package/dist/exec/safe-spawn.js +6 -1
- package/dist/exec/safe-spawn.js.map +1 -1
- package/dist/skills/bundled.d.ts +5 -0
- package/dist/skills/bundled.d.ts.map +1 -0
- package/dist/skills/bundled.js +34 -0
- package/dist/skills/bundled.js.map +1 -0
- package/dist/skills/commands/add.d.ts +1 -3
- package/dist/skills/commands/add.d.ts.map +1 -1
- package/dist/skills/commands/add.js +20 -3
- package/dist/skills/commands/add.js.map +1 -1
- package/dist/skills/tests/bundled.spec.d.ts +2 -0
- package/dist/skills/tests/bundled.spec.d.ts.map +1 -0
- package/dist/skills/tests/bundled.spec.js +24 -0
- package/dist/skills/tests/bundled.spec.js.map +1 -0
- package/dist/types/UpdateManifest.types.d.ts +1 -1
- package/dist/types/UpdateManifest.types.d.ts.map +1 -1
- package/dist/utils/dag-converter.js +1 -1
- package/dist/utils/dag-converter.js.map +1 -1
- package/dist/utils/project-detector.d.ts +1 -0
- package/dist/utils/project-detector.d.ts.map +1 -1
- package/dist/utils/project-detector.js +8 -0
- package/dist/utils/project-detector.js.map +1 -1
- package/dist/utils/project-generator.d.ts +1 -1
- package/dist/utils/project-generator.d.ts.map +1 -1
- package/dist/utils/project-generator.js +23 -2
- package/dist/utils/project-generator.js.map +1 -1
- package/dist/utils/templates.d.ts +2 -0
- package/dist/utils/templates.d.ts.map +1 -1
- package/dist/utils/templates.js +74 -0
- package/dist/utils/templates.js.map +1 -1
- package/dist/verification/__tests__/safe-spawn.test.js +12 -0
- package/dist/verification/__tests__/safe-spawn.test.js.map +1 -1
- package/package.json +2 -1
- package/skills/dare-ax/generator.ts +325 -0
- package/skills/dare-ax/index.ts +19 -0
- package/skills/dare-ax/metrics.ts +352 -0
- package/skills/dare-ax/package-lock.json +1855 -0
- package/skills/dare-ax/package.json +50 -0
- package/skills/dare-ax/secret-detector.ts +123 -0
- package/skills/dare-ax/skill.yml +19 -0
- package/skills/dare-ax/templates/llms.txt.jinja2 +80 -0
- package/skills/dare-ax/tests/generator.spec.ts +193 -0
- package/skills/dare-ax/tests/metrics.spec.ts +394 -0
- package/skills/dare-ax/tests/validator.spec.ts +298 -0
- package/skills/dare-ax/tsconfig.json +18 -0
- package/skills/dare-ax/types.ts +79 -0
- package/skills/dare-ax/validator.ts +238 -0
- package/skills/dare-frontend-design/generator.ts +616 -0
- package/skills/dare-frontend-design/index.ts +25 -0
- package/skills/dare-frontend-design/linter.ts +227 -0
- package/skills/dare-frontend-design/metrics.ts +82 -0
- package/skills/dare-frontend-design/package-lock.json +1855 -0
- package/skills/dare-frontend-design/package.json +43 -0
- package/skills/dare-frontend-design/skill.yml +20 -0
- package/skills/dare-frontend-design/tests/frontend_design.spec.ts +435 -0
- package/skills/dare-frontend-design/tsconfig.json +18 -0
- package/skills/dare-frontend-design/types.ts +62 -0
- package/skills/dare-layered-design/generator.ts +740 -0
- package/skills/dare-layered-design/index.ts +17 -0
- package/skills/dare-layered-design/linter.ts +462 -0
- package/skills/dare-layered-design/metrics.ts +409 -0
- package/skills/dare-layered-design/package-lock.json +1855 -0
- package/skills/dare-layered-design/package.json +50 -0
- package/skills/dare-layered-design/skill.yml +35 -0
- package/skills/dare-layered-design/tests/generator.spec.ts +156 -0
- package/skills/dare-layered-design/tests/linter.spec.ts +255 -0
- package/skills/dare-layered-design/tests/metrics.spec.ts +286 -0
- package/skills/dare-layered-design/tsconfig.json +18 -0
- package/skills/dare-layered-design/types.ts +48 -0
- package/skills/dare-llm-integration/cache/llm_cache.ts +122 -0
- package/skills/dare-llm-integration/index.ts +49 -0
- package/skills/dare-llm-integration/metrics.ts +107 -0
- package/skills/dare-llm-integration/package-lock.json +1855 -0
- package/skills/dare-llm-integration/package.json +49 -0
- package/skills/dare-llm-integration/prompts/prompt_loader.ts +258 -0
- package/skills/dare-llm-integration/providers/anthropic_provider.ts +159 -0
- package/skills/dare-llm-integration/providers/dummy_provider.ts +113 -0
- package/skills/dare-llm-integration/providers/llm_provider.ts +6 -0
- package/skills/dare-llm-integration/providers/openai_provider.ts +215 -0
- package/skills/dare-llm-integration/rate_limit/token_bucket.ts +86 -0
- package/skills/dare-llm-integration/skill.yml +23 -0
- package/skills/dare-llm-integration/tests/fixtures/greet_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v1.jinja2 +1 -0
- package/skills/dare-llm-integration/tests/fixtures/summarize_v2.jinja2 +3 -0
- package/skills/dare-llm-integration/tests/llm_integration.spec.ts +657 -0
- package/skills/dare-llm-integration/tsconfig.json +23 -0
- package/skills/dare-llm-integration/types.ts +91 -0
- package/skills/dare-llm-integration/validators/output_validator.ts +200 -0
- package/skills/dare-quality-telemetry/collect.ts +134 -0
- package/skills/dare-quality-telemetry/collectors/dare_ax_collector.ts +301 -0
- package/skills/dare-quality-telemetry/collectors/dare_layered_design_collector.ts +406 -0
- package/skills/dare-quality-telemetry/collectors/index.ts +24 -0
- package/skills/dare-quality-telemetry/github_actions_template.ts +25 -0
- package/skills/dare-quality-telemetry/index.ts +18 -0
- package/skills/dare-quality-telemetry/metrics.ts +137 -0
- package/skills/dare-quality-telemetry/package-lock.json +1855 -0
- package/skills/dare-quality-telemetry/package.json +48 -0
- package/skills/dare-quality-telemetry/regression.ts +60 -0
- package/skills/dare-quality-telemetry/reporter.ts +132 -0
- package/skills/dare-quality-telemetry/skill.yml +18 -0
- package/skills/dare-quality-telemetry/tests/quality_telemetry.spec.ts +885 -0
- package/skills/dare-quality-telemetry/tsconfig.json +19 -0
- package/skills/dare-quality-telemetry/types.ts +41 -0
- package/skills/dare-realtime/event_registry.ts +101 -0
- package/skills/dare-realtime/index.ts +30 -0
- package/skills/dare-realtime/metrics.ts +84 -0
- package/skills/dare-realtime/package-lock.json +1855 -0
- package/skills/dare-realtime/package.json +43 -0
- package/skills/dare-realtime/reconnect_strategy.ts +85 -0
- package/skills/dare-realtime/schema_validator.ts +80 -0
- package/skills/dare-realtime/skill.yml +21 -0
- package/skills/dare-realtime/subscription_manager.ts +106 -0
- package/skills/dare-realtime/tests/realtime.spec.ts +482 -0
- package/skills/dare-realtime/tsconfig.json +18 -0
- package/skills/dare-realtime/types.ts +51 -0
- package/templates/ide/antigravity/.agents/skills/dare-ai/SKILL.md +17 -0
- package/templates/ide/antigravity/.agents/skills/dare-blueprint/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-design/SKILL.md +2 -0
- package/templates/ide/antigravity/.agents/skills/dare-dna/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-migrate/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-patterns/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-refine/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-reverse/SKILL.md +3 -0
- package/templates/ide/antigravity/.agents/skills/dare-review/SKILL.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-ai.md +17 -0
- package/templates/ide/claude/.claude/commands/dare-blueprint.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-design.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-dna.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-migrate.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-patterns.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-refine.md +3 -0
- package/templates/ide/claude/.claude/commands/dare-reverse.md +2 -0
- package/templates/ide/claude/.claude/commands/dare-review.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-ai.md +17 -0
- package/templates/ide/cursor/.cursor/commands/dare-blueprint.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-design.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-dna.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-migrate.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-patterns.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-refine.md +3 -0
- package/templates/ide/cursor/.cursor/commands/dare-reverse.md +2 -0
- package/templates/ide/cursor/.cursor/commands/dare-review.md +3 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dewtech/dare-layered-design",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "dare-layered-design — 4-layer architecture skill for DARE Method v3.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"templates",
|
|
17
|
+
"skill.yml"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:watch": "vitest",
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@types/node": "^20.19.41",
|
|
27
|
+
"typescript": "^5.9.3",
|
|
28
|
+
"vitest": "^1.6.1"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"dare",
|
|
32
|
+
"dare-method",
|
|
33
|
+
"dare-layered-design",
|
|
34
|
+
"layered-architecture",
|
|
35
|
+
"clean-architecture",
|
|
36
|
+
"handlers",
|
|
37
|
+
"services",
|
|
38
|
+
"repositories"
|
|
39
|
+
],
|
|
40
|
+
"author": "Dewtech Technologies",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/dewtech-technologies/dare-method.git",
|
|
45
|
+
"directory": "packages/skills/dare-layered-design"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: dare-layered-design
|
|
2
|
+
version: 1.0.0
|
|
3
|
+
description: >
|
|
4
|
+
Layered Design skill — enforces a strict 4-layer architecture (Handlers → Services →
|
|
5
|
+
Repositories → Models) across all DARE projects, regardless of language or framework.
|
|
6
|
+
Inspired by Vladimir Dementyev's "Layered Design for Ruby on Rails Applications" and
|
|
7
|
+
Evil Martians methodology.
|
|
8
|
+
author: Dewtech Technologies
|
|
9
|
+
license: MIT
|
|
10
|
+
dependencies: {}
|
|
11
|
+
metrics:
|
|
12
|
+
- id: M-01
|
|
13
|
+
description: 100% of Services have unit tests (no real DB or HTTP)
|
|
14
|
+
- id: M-02
|
|
15
|
+
description: 0% Handler→Repository direct calls in codebase
|
|
16
|
+
- id: M-03
|
|
17
|
+
description: 100% of Handlers use dependency injection (no new Service() inside)
|
|
18
|
+
- id: M-04
|
|
19
|
+
description: 100% of Repositories are agnostic to upper layers (no HTTP status codes returned)
|
|
20
|
+
layers:
|
|
21
|
+
- name: handlers
|
|
22
|
+
description: HTTP/gRPC entry points — no business logic
|
|
23
|
+
aliases: [controllers, routers, routes]
|
|
24
|
+
- name: services
|
|
25
|
+
description: Business logic — one operation per class/module
|
|
26
|
+
aliases: [use_cases, interactors, commands]
|
|
27
|
+
- name: repositories
|
|
28
|
+
description: Data access — abstractions over DB/cache/external APIs
|
|
29
|
+
aliases: [repos, data_access, stores]
|
|
30
|
+
- name: models
|
|
31
|
+
description: Domain objects — no HTTP or DB concerns
|
|
32
|
+
aliases: [entities, domain, structs]
|
|
33
|
+
- name: presenters
|
|
34
|
+
description: Serializers — converts Models to JSON/XML/etc
|
|
35
|
+
aliases: [serializers, formatters, views]
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-layered-design — generator tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { LayeredDesignGenerator } from '../generator.js';
|
|
10
|
+
|
|
11
|
+
describe('LayeredDesignGenerator', () => {
|
|
12
|
+
let tmpDir: string;
|
|
13
|
+
let generator: LayeredDesignGenerator;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dare-scaffold-test-'));
|
|
17
|
+
generator = new LayeredDesignGenerator();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('scaffold()', () => {
|
|
25
|
+
it('creates all 5 layer directories', () => {
|
|
26
|
+
const result = generator.scaffold(tmpDir);
|
|
27
|
+
|
|
28
|
+
const expectedDirs = ['handlers', 'services', 'repositories', 'models', 'presenters'];
|
|
29
|
+
for (const dir of expectedDirs) {
|
|
30
|
+
const fullPath = path.join(tmpDir, 'src', dir);
|
|
31
|
+
expect(fs.existsSync(fullPath), `Expected ${dir} to exist`).toBe(true);
|
|
32
|
+
expect(result.createdDirs).toContain(fullPath);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('creates README.md in each layer directory', () => {
|
|
37
|
+
generator.scaffold(tmpDir);
|
|
38
|
+
|
|
39
|
+
const layers = ['handlers', 'services', 'repositories', 'models', 'presenters'];
|
|
40
|
+
for (const layer of layers) {
|
|
41
|
+
const readmePath = path.join(tmpDir, 'src', layer, 'README.md');
|
|
42
|
+
expect(fs.existsSync(readmePath), `Expected README.md in ${layer}`).toBe(true);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('creates .gitkeep in each layer when withExamples=false', () => {
|
|
47
|
+
generator.scaffold(tmpDir, { withExamples: false });
|
|
48
|
+
|
|
49
|
+
const layers = ['handlers', 'services', 'repositories', 'models', 'presenters'];
|
|
50
|
+
for (const layer of layers) {
|
|
51
|
+
const gitkeepPath = path.join(tmpDir, 'src', layer, '.gitkeep');
|
|
52
|
+
expect(fs.existsSync(gitkeepPath), `Expected .gitkeep in ${layer}`).toBe(true);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('uses custom srcDir', () => {
|
|
57
|
+
generator.scaffold(tmpDir, { srcDir: 'app' });
|
|
58
|
+
|
|
59
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'handlers'))).toBe(true);
|
|
60
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'services'))).toBe(true);
|
|
61
|
+
expect(fs.existsSync(path.join(tmpDir, 'src'))).toBe(false);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('creates ARCHITECTURE.md in srcDir', () => {
|
|
65
|
+
generator.scaffold(tmpDir);
|
|
66
|
+
|
|
67
|
+
const archPath = path.join(tmpDir, 'src', 'ARCHITECTURE.md');
|
|
68
|
+
expect(fs.existsSync(archPath)).toBe(true);
|
|
69
|
+
const content = fs.readFileSync(archPath, 'utf-8');
|
|
70
|
+
expect(content).toContain('Handler → Service → Repository → Model');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('creates TypeScript example files when withExamples=true', () => {
|
|
74
|
+
generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'typescript' });
|
|
75
|
+
|
|
76
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'handlers', 'user_handler.ts'))).toBe(true);
|
|
77
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'services', 'create_user_service.ts'))).toBe(true);
|
|
78
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'repositories', 'user_repository.ts'))).toBe(true);
|
|
79
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'models', 'user.ts'))).toBe(true);
|
|
80
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'presenters', 'user_presenter.ts'))).toBe(true);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('creates Ruby example files when withExamples=true and language=ruby', () => {
|
|
84
|
+
generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'ruby', srcDir: 'app' });
|
|
85
|
+
|
|
86
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'handlers', 'user_handler.rb'))).toBe(true);
|
|
87
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'services', 'create_user_service.rb'))).toBe(true);
|
|
88
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'repositories', 'user_repository.rb'))).toBe(true);
|
|
89
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'models', 'user.rb'))).toBe(true);
|
|
90
|
+
expect(fs.existsSync(path.join(tmpDir, 'app', 'presenters', 'user_presenter.rb'))).toBe(true);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('TypeScript handler example does not import Repository', () => {
|
|
94
|
+
generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'typescript' });
|
|
95
|
+
|
|
96
|
+
const content = fs.readFileSync(
|
|
97
|
+
path.join(tmpDir, 'src', 'handlers', 'user_handler.ts'),
|
|
98
|
+
'utf-8'
|
|
99
|
+
);
|
|
100
|
+
expect(content).not.toContain('import.*Repository');
|
|
101
|
+
expect(content).toContain('Service');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('TypeScript service example mentions DI (no new Repository)', () => {
|
|
105
|
+
generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'product', language: 'typescript' });
|
|
106
|
+
|
|
107
|
+
const content = fs.readFileSync(
|
|
108
|
+
path.join(tmpDir, 'src', 'services', 'create_product_service.ts'),
|
|
109
|
+
'utf-8'
|
|
110
|
+
);
|
|
111
|
+
// Should receive repository via constructor, not instantiate it
|
|
112
|
+
expect(content).toContain('constructor');
|
|
113
|
+
expect(content).not.toMatch(/new\s+\w+Repository\s*\(/);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('TypeScript repository example includes InMemory implementation', () => {
|
|
117
|
+
generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user', language: 'typescript' });
|
|
118
|
+
|
|
119
|
+
const content = fs.readFileSync(
|
|
120
|
+
path.join(tmpDir, 'src', 'repositories', 'user_repository.ts'),
|
|
121
|
+
'utf-8'
|
|
122
|
+
);
|
|
123
|
+
expect(content).toContain('InMemory');
|
|
124
|
+
expect(content).toContain('interface UserRepository');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('does not overwrite existing files on second scaffold', () => {
|
|
128
|
+
generator.scaffold(tmpDir);
|
|
129
|
+
|
|
130
|
+
// Manually modify a README
|
|
131
|
+
const readmePath = path.join(tmpDir, 'src', 'handlers', 'README.md');
|
|
132
|
+
fs.writeFileSync(readmePath, '# Custom README\n', 'utf-8');
|
|
133
|
+
|
|
134
|
+
// Scaffold again
|
|
135
|
+
generator.scaffold(tmpDir);
|
|
136
|
+
|
|
137
|
+
// Custom README should be preserved
|
|
138
|
+
const content = fs.readFileSync(readmePath, 'utf-8');
|
|
139
|
+
expect(content).toContain('Custom README');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('returns createdFiles list with README.md paths', () => {
|
|
143
|
+
const result = generator.scaffold(tmpDir);
|
|
144
|
+
|
|
145
|
+
expect(result.createdFiles.some((f) => f.endsWith('README.md'))).toBe(true);
|
|
146
|
+
expect(result.createdFiles.filter((f) => f.endsWith('README.md'))).toHaveLength(5);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('handles multi-word entity names with snake_case convention', () => {
|
|
150
|
+
generator.scaffold(tmpDir, { withExamples: true, exampleEntity: 'user-profile', language: 'typescript' });
|
|
151
|
+
|
|
152
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'handlers', 'user_profile_handler.ts'))).toBe(true);
|
|
153
|
+
expect(fs.existsSync(path.join(tmpDir, 'src', 'models', 'user_profile.ts'))).toBe(true);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
});
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dare-layered-design — linter tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { LayeredDesignLinter } from '../linter.js';
|
|
10
|
+
|
|
11
|
+
describe('LayeredDesignLinter', () => {
|
|
12
|
+
let tmpDir: string;
|
|
13
|
+
let linter: LayeredDesignLinter;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dare-linter-test-'));
|
|
17
|
+
linter = new LayeredDesignLinter();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// ── TypeScript / JavaScript ──────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
describe('TypeScript handler violations', () => {
|
|
27
|
+
function createHandlerFile(name: string, content: string): string {
|
|
28
|
+
const handlersDir = path.join(tmpDir, 'src', 'handlers');
|
|
29
|
+
fs.mkdirSync(handlersDir, { recursive: true });
|
|
30
|
+
const file = path.join(handlersDir, name);
|
|
31
|
+
fs.writeFileSync(file, content, 'utf-8');
|
|
32
|
+
// Create tsconfig.json so language is detected
|
|
33
|
+
fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}', 'utf-8');
|
|
34
|
+
return file;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
it('detects import of Repository in handler (TS)', () => {
|
|
38
|
+
createHandlerFile(
|
|
39
|
+
'user_handler.ts',
|
|
40
|
+
`import { UserRepository } from '../repositories/user_repository';
|
|
41
|
+
export class UserHandler {
|
|
42
|
+
async create(req: Request, res: Response) {
|
|
43
|
+
const repo = new UserRepository();
|
|
44
|
+
res.json(await repo.save(req.body));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
51
|
+
expect(result.pass).toBe(false);
|
|
52
|
+
expect(result.violations.length).toBeGreaterThan(0);
|
|
53
|
+
expect(result.violations[0].message).toContain('Repository');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('detects direct instantiation of Repository in handler (TS)', () => {
|
|
57
|
+
createHandlerFile(
|
|
58
|
+
'order_handler.ts',
|
|
59
|
+
`export class OrderHandler {
|
|
60
|
+
async create(req: Request, res: Response) {
|
|
61
|
+
const repo = new OrderRepository(); // VIOLATION
|
|
62
|
+
const order = await repo.save(req.body);
|
|
63
|
+
res.json(order);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
`
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
70
|
+
expect(result.pass).toBe(false);
|
|
71
|
+
expect(result.violations.some((v) => v.message.includes('instantiates'))).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('detects direct repository method call in handler (TS)', () => {
|
|
75
|
+
createHandlerFile(
|
|
76
|
+
'product_handler.ts',
|
|
77
|
+
`const userRepository = new UserRepository();
|
|
78
|
+
|
|
79
|
+
export async function getUser(req: Request, res: Response) {
|
|
80
|
+
const user = await userRepository.findById(req.params.id); // VIOLATION
|
|
81
|
+
res.json(user);
|
|
82
|
+
}
|
|
83
|
+
`
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
87
|
+
expect(result.pass).toBe(false);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('does not flag a handler that only uses services (no violation)', () => {
|
|
91
|
+
createHandlerFile(
|
|
92
|
+
'user_handler.ts',
|
|
93
|
+
`export class UserHandler {
|
|
94
|
+
constructor(private readonly createUserService: CreateUserService) {}
|
|
95
|
+
|
|
96
|
+
async create(req: Request, res: Response) {
|
|
97
|
+
const user = await this.createUserService.execute(req.body);
|
|
98
|
+
res.status(201).json(user);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
`
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
105
|
+
expect(result.pass).toBe(true);
|
|
106
|
+
expect(result.violations).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('does not flag import of Service in handler', () => {
|
|
110
|
+
createHandlerFile(
|
|
111
|
+
'user_handler.ts',
|
|
112
|
+
`import { CreateUserService } from '../services/create_user_service';
|
|
113
|
+
|
|
114
|
+
export class UserHandler {
|
|
115
|
+
constructor(private readonly service: CreateUserService) {}
|
|
116
|
+
|
|
117
|
+
async create(req: Request, res: Response) {
|
|
118
|
+
const user = await this.service.execute(req.body);
|
|
119
|
+
res.json(user);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
`
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
126
|
+
expect(result.pass).toBe(true);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('skips commented-out violations', () => {
|
|
130
|
+
createHandlerFile(
|
|
131
|
+
'user_handler.ts',
|
|
132
|
+
`export class UserHandler {
|
|
133
|
+
async create(req: Request, res: Response) {
|
|
134
|
+
// const repo = new UserRepository(); // This would be a violation if uncommented
|
|
135
|
+
const user = await this.service.execute(req.body);
|
|
136
|
+
res.json(user);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
`
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
143
|
+
expect(result.pass).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ── Ruby ──────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
describe('Ruby handler violations', () => {
|
|
150
|
+
function createRubyHandlerFile(name: string, content: string): string {
|
|
151
|
+
const controllersDir = path.join(tmpDir, 'app', 'controllers');
|
|
152
|
+
fs.mkdirSync(controllersDir, { recursive: true });
|
|
153
|
+
const file = path.join(controllersDir, name);
|
|
154
|
+
fs.writeFileSync(file, content, 'utf-8');
|
|
155
|
+
fs.writeFileSync(path.join(tmpDir, 'Gemfile'), 'source "https://rubygems.org"', 'utf-8');
|
|
156
|
+
return file;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
it('detects Repository call in Rails controller', () => {
|
|
160
|
+
createRubyHandlerFile(
|
|
161
|
+
'users_controller.rb',
|
|
162
|
+
`class UsersController < ApplicationController
|
|
163
|
+
def create
|
|
164
|
+
user = UserRepository.save(user_params) # VIOLATION
|
|
165
|
+
render json: user
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
`
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const result = linter.lint(tmpDir, 'ruby');
|
|
172
|
+
expect(result.pass).toBe(false);
|
|
173
|
+
expect(result.violations.length).toBeGreaterThan(0);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it('passes when controller only calls service', () => {
|
|
177
|
+
createRubyHandlerFile(
|
|
178
|
+
'users_controller.rb',
|
|
179
|
+
`class UsersController < ApplicationController
|
|
180
|
+
def initialize(create_user_service:)
|
|
181
|
+
@create_user_service = create_user_service
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def create
|
|
185
|
+
user = @create_user_service.call(user_params)
|
|
186
|
+
render json: user, status: :created
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
`
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
const result = linter.lint(tmpDir, 'ruby');
|
|
193
|
+
expect(result.pass).toBe(true);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// ── lintFile() ────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
describe('lintFile()', () => {
|
|
200
|
+
it('lints a single TypeScript handler file', () => {
|
|
201
|
+
const file = path.join(tmpDir, 'user_handler.ts');
|
|
202
|
+
fs.writeFileSync(
|
|
203
|
+
file,
|
|
204
|
+
`import { UserRepository } from '../repositories/user_repository';\n`,
|
|
205
|
+
'utf-8'
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
const result = linter.lintFile(file, 'typescript');
|
|
209
|
+
expect(result.pass).toBe(false);
|
|
210
|
+
expect(result.violations).toHaveLength(1);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it('returns pass=true for clean handler file', () => {
|
|
214
|
+
const file = path.join(tmpDir, 'user_handler.ts');
|
|
215
|
+
fs.writeFileSync(
|
|
216
|
+
file,
|
|
217
|
+
`import { CreateUserService } from '../services/create_user_service';\n`,
|
|
218
|
+
'utf-8'
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
const result = linter.lintFile(file, 'typescript');
|
|
222
|
+
expect(result.pass).toBe(true);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('returns filesScanned=1 for single file lint', () => {
|
|
226
|
+
const file = path.join(tmpDir, 'user_handler.ts');
|
|
227
|
+
fs.writeFileSync(file, 'export const x = 1;\n', 'utf-8');
|
|
228
|
+
|
|
229
|
+
const result = linter.lintFile(file, 'typescript');
|
|
230
|
+
expect(result.filesScanned).toBe(1);
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// ── Empty project ─────────────────────────────────────────────────────────
|
|
235
|
+
|
|
236
|
+
it('returns pass=true with 0 violations for empty project', () => {
|
|
237
|
+
const result = linter.lint(tmpDir);
|
|
238
|
+
expect(result.pass).toBe(true);
|
|
239
|
+
expect(result.violations).toHaveLength(0);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('reports filesScanned > 0 when handler files exist', () => {
|
|
243
|
+
const handlersDir = path.join(tmpDir, 'src', 'handlers');
|
|
244
|
+
fs.mkdirSync(handlersDir, { recursive: true });
|
|
245
|
+
fs.writeFileSync(
|
|
246
|
+
path.join(handlersDir, 'clean_handler.ts'),
|
|
247
|
+
'export const handler = () => {};\n',
|
|
248
|
+
'utf-8'
|
|
249
|
+
);
|
|
250
|
+
fs.writeFileSync(path.join(tmpDir, 'tsconfig.json'), '{}', 'utf-8');
|
|
251
|
+
|
|
252
|
+
const result = linter.lint(tmpDir, 'typescript');
|
|
253
|
+
expect(result.filesScanned).toBeGreaterThan(0);
|
|
254
|
+
});
|
|
255
|
+
});
|