@pukujan/create-modular-monolith 2.0.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 +43 -0
- package/bin/create-modular-monolith.js +132 -0
- package/package.json +39 -0
- package/template/README.md +73 -0
- package/template/backend/package-lock.json +882 -0
- package/template/backend/package.json +20 -0
- package/template/backend/scripts/check-module-boundaries.mjs +69 -0
- package/template/backend/scripts/check-module-layers.mjs +152 -0
- package/template/backend/src/core/module-loader.js +35 -0
- package/template/backend/src/core/server.js +24 -0
- package/template/backend/src/modules/.gitkeep +0 -0
- package/template/backend/src/modules/_reference/README.md +11 -0
- package/template/backend/src/modules/_reference/adapters/README.md +3 -0
- package/template/backend/src/modules/_reference/config/index.js +4 -0
- package/template/backend/src/modules/_reference/domain/README.md +3 -0
- package/template/backend/src/modules/_reference/evals/README.md +6 -0
- package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +12 -0
- package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +25 -0
- package/template/backend/src/modules/_reference/events/index.js +4 -0
- package/template/backend/src/modules/_reference/index.js +9 -0
- package/template/backend/src/modules/_reference/prompts/manifest.json +14 -0
- package/template/backend/src/modules/_reference/prompts/templates/example.prompt.js +7 -0
- package/template/backend/src/modules/_reference/repositories/.gitkeep +0 -0
- package/template/backend/src/modules/_reference/routes/health.routes.js +10 -0
- package/template/backend/src/modules/_reference/routes/index.js +8 -0
- package/template/backend/src/modules/_reference/schemas/health.schema.js +8 -0
- package/template/backend/src/modules/_reference/services/health.service.js +7 -0
- package/template/backend/src/modules/_reference/tests/integration/health.routes.test.js +20 -0
- package/template/backend/src/modules/_reference/tests/unit/health.service.test.js +9 -0
- package/template/backend/src/modules/_reference/utils/index.js +3 -0
- package/template/backend/src/shared/ai/prompt-registry.js +42 -0
- package/template/backend/src/shared/events/index.js +8 -0
- package/template/backend/src/shared/http/errors.js +10 -0
- package/template/backend/src/shared/testing/create-test-app.js +13 -0
- package/template/docs/DEVLOG_V2.md +369 -0
- package/template/docs/PUBLISHING.md +39 -0
- package/template/docs/README.md +13 -0
- package/template/docs/STARTER_PACK.md +98 -0
- package/template/docs/architecture/ARCHITECTURE_GUARDRAILS.md +74 -0
- package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +164 -0
- package/template/frontend/index.html +12 -0
- package/template/frontend/package-lock.json +1724 -0
- package/template/frontend/package.json +21 -0
- package/template/frontend/src/core/App.jsx +35 -0
- package/template/frontend/src/core/moduleRegistry.jsx +39 -0
- package/template/frontend/src/index.css +53 -0
- package/template/frontend/src/main.jsx +10 -0
- package/template/frontend/src/modules/.gitkeep +0 -0
- package/template/frontend/src/modules/_reference/README.md +3 -0
- package/template/frontend/src/modules/_reference/components/ModuleHealthCard.jsx +14 -0
- package/template/frontend/src/modules/_reference/hooks/use-module-health.js +27 -0
- package/template/frontend/src/modules/_reference/index.jsx +7 -0
- package/template/frontend/src/modules/_reference/pages/_referencePage.jsx +11 -0
- package/template/frontend/src/modules/_reference/prompts/README.md +3 -0
- package/template/frontend/src/modules/_reference/schemas/health.schema.js +3 -0
- package/template/frontend/src/modules/_reference/services/health-api.js +5 -0
- package/template/frontend/src/modules/_reference/tests/unit/health.schema.test.js +8 -0
- package/template/frontend/src/modules/_reference/utils/index.js +3 -0
- package/template/frontend/src/shared/api/client.js +10 -0
- package/template/frontend/vite.config.js +6 -0
- package/template/package.json +16 -0
- package/template/scripts/lib/module-scaffold.mjs +409 -0
- package/template/scripts/new-module.mjs +58 -0
- package/template/scripts/run-module-evals.mjs +43 -0
- package/template/scripts/sync-cli-template.mjs +44 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "modular-monolith-starter-frontend",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"test": "node --test 'src/modules/**/tests/**/*.test.js'"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"react": "^18.3.1",
|
|
14
|
+
"react-dom": "^18.3.1",
|
|
15
|
+
"react-router-dom": "^6.30.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@vitejs/plugin-react": "^4.3.1",
|
|
19
|
+
"vite": "^5.4.10"
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { BrowserRouter, NavLink, Navigate, Route, Routes } from "react-router-dom";
|
|
3
|
+
import { moduleRoutes, EmptyModulePage, MissingPage } from "./moduleRegistry.jsx";
|
|
4
|
+
|
|
5
|
+
export function App() {
|
|
6
|
+
return (
|
|
7
|
+
<BrowserRouter>
|
|
8
|
+
<nav className="nav">
|
|
9
|
+
<strong>Modular Litigation Starter</strong>
|
|
10
|
+
<span className="badge">{moduleRoutes.length} modules</span>
|
|
11
|
+
{moduleRoutes.map((entry) => (
|
|
12
|
+
<NavLink key={entry.route} to={entry.route}>
|
|
13
|
+
{entry.label}
|
|
14
|
+
</NavLink>
|
|
15
|
+
))}
|
|
16
|
+
</nav>
|
|
17
|
+
|
|
18
|
+
<main className="container">
|
|
19
|
+
<Routes>
|
|
20
|
+
{moduleRoutes.length > 0 ? (
|
|
21
|
+
<Route path="/" element={<Navigate to={moduleRoutes[0].route} replace />} />
|
|
22
|
+
) : (
|
|
23
|
+
<Route path="/" element={<EmptyModulePage />} />
|
|
24
|
+
)}
|
|
25
|
+
|
|
26
|
+
{moduleRoutes.map((entry) => (
|
|
27
|
+
<Route key={entry.route} path={entry.route} element={<entry.Component />} />
|
|
28
|
+
))}
|
|
29
|
+
|
|
30
|
+
<Route path="*" element={<MissingPage />} />
|
|
31
|
+
</Routes>
|
|
32
|
+
</main>
|
|
33
|
+
</BrowserRouter>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
const files = import.meta.glob("../modules/*/index.jsx", { eager: true });
|
|
4
|
+
|
|
5
|
+
function toRecord(mod) {
|
|
6
|
+
const value = mod?.default || mod;
|
|
7
|
+
if (!value || !value.route || !value.label || !value.Component) return null;
|
|
8
|
+
return {
|
|
9
|
+
route: value.route,
|
|
10
|
+
label: value.label,
|
|
11
|
+
Component: value.Component
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const moduleRoutes = Object.entries(files)
|
|
16
|
+
.filter(([path]) => !/\/modules\/_/.test(path))
|
|
17
|
+
.map(([, mod]) => toRecord(mod))
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.sort((a, b) => a.label.localeCompare(b.label));
|
|
20
|
+
|
|
21
|
+
export function EmptyModulePage() {
|
|
22
|
+
return (
|
|
23
|
+
<div className="card">
|
|
24
|
+
<h2>No modules yet</h2>
|
|
25
|
+
<p className="muted">
|
|
26
|
+
Add a module with: <strong>node scripts/new-module.mjs my-module --label "My Module"</strong>
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function MissingPage() {
|
|
33
|
+
return (
|
|
34
|
+
<div className="card">
|
|
35
|
+
<h2>Not Found</h2>
|
|
36
|
+
<p className="muted">This route is not registered by any module.</p>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: Inter, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
* {
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
background: #0b1020;
|
|
12
|
+
color: #f5f7ff;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
a {
|
|
16
|
+
color: inherit;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.container {
|
|
20
|
+
max-width: 1080px;
|
|
21
|
+
margin: 0 auto;
|
|
22
|
+
padding: 24px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.nav {
|
|
26
|
+
display: flex;
|
|
27
|
+
gap: 12px;
|
|
28
|
+
flex-wrap: wrap;
|
|
29
|
+
align-items: center;
|
|
30
|
+
padding: 16px 24px;
|
|
31
|
+
border-bottom: 1px solid #1f2b4a;
|
|
32
|
+
background: #121a31;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.badge {
|
|
36
|
+
font-size: 12px;
|
|
37
|
+
padding: 4px 8px;
|
|
38
|
+
border: 1px solid #3657a7;
|
|
39
|
+
border-radius: 999px;
|
|
40
|
+
color: #a8c1ff;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.card {
|
|
44
|
+
border: 1px solid #233257;
|
|
45
|
+
background: #121a31;
|
|
46
|
+
border-radius: 10px;
|
|
47
|
+
padding: 16px;
|
|
48
|
+
margin-top: 16px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.muted {
|
|
52
|
+
color: #a8b4d8;
|
|
53
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useModuleHealth } from "../hooks/use-module-health.js";
|
|
2
|
+
|
|
3
|
+
export function ModuleHealthCard() {
|
|
4
|
+
const { data, error, loading } = useModuleHealth();
|
|
5
|
+
|
|
6
|
+
if (loading) return <p className="muted">Checking backend…</p>;
|
|
7
|
+
if (error) return <p className="muted">Backend unavailable: {error.message}</p>;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<p>
|
|
11
|
+
Backend health: <code>{data?.status}</code> ({data?.module})
|
|
12
|
+
</p>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { fetchModuleHealth } from "../services/health-api.js";
|
|
3
|
+
|
|
4
|
+
export function useModuleHealth() {
|
|
5
|
+
const [data, setData] = useState(null);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
const [loading, setLoading] = useState(true);
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
let active = true;
|
|
11
|
+
fetchModuleHealth()
|
|
12
|
+
.then((result) => {
|
|
13
|
+
if (active) setData(result);
|
|
14
|
+
})
|
|
15
|
+
.catch((err) => {
|
|
16
|
+
if (active) setError(err);
|
|
17
|
+
})
|
|
18
|
+
.finally(() => {
|
|
19
|
+
if (active) setLoading(false);
|
|
20
|
+
});
|
|
21
|
+
return () => {
|
|
22
|
+
active = false;
|
|
23
|
+
};
|
|
24
|
+
}, []);
|
|
25
|
+
|
|
26
|
+
return { data, error, loading };
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ModuleHealthCard } from "../components/ModuleHealthCard.jsx";
|
|
2
|
+
|
|
3
|
+
export function _referencePage() {
|
|
4
|
+
return (
|
|
5
|
+
<section className="card">
|
|
6
|
+
<h2>Reference (example)</h2>
|
|
7
|
+
<p className="muted">Module shell — extend pages, hooks, and services.</p>
|
|
8
|
+
<ModuleHealthCard />
|
|
9
|
+
</section>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { isHealthResponse } from "../../schemas/health.schema.js";
|
|
4
|
+
|
|
5
|
+
test("isHealthResponse validates shape", () => {
|
|
6
|
+
assert.equal(isHealthResponse({ status: "ok" }), true);
|
|
7
|
+
assert.equal(isHealthResponse(null), false);
|
|
8
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const BASE_URL = import.meta.env.VITE_API_BASE_URL || "http://localhost:3001";
|
|
2
|
+
|
|
3
|
+
export async function apiGet(path) {
|
|
4
|
+
const response = await fetch(`${BASE_URL}${path}`);
|
|
5
|
+
if (!response.ok) {
|
|
6
|
+
const text = await response.text();
|
|
7
|
+
throw new Error(text || `Request failed: ${response.status}`);
|
|
8
|
+
}
|
|
9
|
+
return response.json();
|
|
10
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "modular-monolith-starter",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"sync:cli-template": "node scripts/sync-cli-template.mjs",
|
|
7
|
+
"dev:backend": "npm --prefix backend run dev",
|
|
8
|
+
"dev:frontend": "npm --prefix frontend run dev",
|
|
9
|
+
"lint:boundaries": "npm --prefix backend run lint:boundaries",
|
|
10
|
+
"lint:layers": "npm --prefix backend run lint:layers",
|
|
11
|
+
"lint:architecture": "npm --prefix backend run lint:architecture",
|
|
12
|
+
"test": "npm --prefix backend test && npm --prefix frontend test",
|
|
13
|
+
"test:evals": "node scripts/run-module-evals.mjs",
|
|
14
|
+
"new:module": "node scripts/new-module.mjs"
|
|
15
|
+
}
|
|
16
|
+
}
|