@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.
Files changed (65) hide show
  1. package/README.md +43 -0
  2. package/bin/create-modular-monolith.js +132 -0
  3. package/package.json +39 -0
  4. package/template/README.md +73 -0
  5. package/template/backend/package-lock.json +882 -0
  6. package/template/backend/package.json +20 -0
  7. package/template/backend/scripts/check-module-boundaries.mjs +69 -0
  8. package/template/backend/scripts/check-module-layers.mjs +152 -0
  9. package/template/backend/src/core/module-loader.js +35 -0
  10. package/template/backend/src/core/server.js +24 -0
  11. package/template/backend/src/modules/.gitkeep +0 -0
  12. package/template/backend/src/modules/_reference/README.md +11 -0
  13. package/template/backend/src/modules/_reference/adapters/README.md +3 -0
  14. package/template/backend/src/modules/_reference/config/index.js +4 -0
  15. package/template/backend/src/modules/_reference/domain/README.md +3 -0
  16. package/template/backend/src/modules/_reference/evals/README.md +6 -0
  17. package/template/backend/src/modules/_reference/evals/datasets/example.cases.json +12 -0
  18. package/template/backend/src/modules/_reference/evals/runners/example.eval.mjs +25 -0
  19. package/template/backend/src/modules/_reference/events/index.js +4 -0
  20. package/template/backend/src/modules/_reference/index.js +9 -0
  21. package/template/backend/src/modules/_reference/prompts/manifest.json +14 -0
  22. package/template/backend/src/modules/_reference/prompts/templates/example.prompt.js +7 -0
  23. package/template/backend/src/modules/_reference/repositories/.gitkeep +0 -0
  24. package/template/backend/src/modules/_reference/routes/health.routes.js +10 -0
  25. package/template/backend/src/modules/_reference/routes/index.js +8 -0
  26. package/template/backend/src/modules/_reference/schemas/health.schema.js +8 -0
  27. package/template/backend/src/modules/_reference/services/health.service.js +7 -0
  28. package/template/backend/src/modules/_reference/tests/integration/health.routes.test.js +20 -0
  29. package/template/backend/src/modules/_reference/tests/unit/health.service.test.js +9 -0
  30. package/template/backend/src/modules/_reference/utils/index.js +3 -0
  31. package/template/backend/src/shared/ai/prompt-registry.js +42 -0
  32. package/template/backend/src/shared/events/index.js +8 -0
  33. package/template/backend/src/shared/http/errors.js +10 -0
  34. package/template/backend/src/shared/testing/create-test-app.js +13 -0
  35. package/template/docs/DEVLOG_V2.md +369 -0
  36. package/template/docs/PUBLISHING.md +39 -0
  37. package/template/docs/README.md +13 -0
  38. package/template/docs/STARTER_PACK.md +98 -0
  39. package/template/docs/architecture/ARCHITECTURE_GUARDRAILS.md +74 -0
  40. package/template/docs/architecture/MODULE_INTERNAL_CONTRACT.md +164 -0
  41. package/template/frontend/index.html +12 -0
  42. package/template/frontend/package-lock.json +1724 -0
  43. package/template/frontend/package.json +21 -0
  44. package/template/frontend/src/core/App.jsx +35 -0
  45. package/template/frontend/src/core/moduleRegistry.jsx +39 -0
  46. package/template/frontend/src/index.css +53 -0
  47. package/template/frontend/src/main.jsx +10 -0
  48. package/template/frontend/src/modules/.gitkeep +0 -0
  49. package/template/frontend/src/modules/_reference/README.md +3 -0
  50. package/template/frontend/src/modules/_reference/components/ModuleHealthCard.jsx +14 -0
  51. package/template/frontend/src/modules/_reference/hooks/use-module-health.js +27 -0
  52. package/template/frontend/src/modules/_reference/index.jsx +7 -0
  53. package/template/frontend/src/modules/_reference/pages/_referencePage.jsx +11 -0
  54. package/template/frontend/src/modules/_reference/prompts/README.md +3 -0
  55. package/template/frontend/src/modules/_reference/schemas/health.schema.js +3 -0
  56. package/template/frontend/src/modules/_reference/services/health-api.js +5 -0
  57. package/template/frontend/src/modules/_reference/tests/unit/health.schema.test.js +8 -0
  58. package/template/frontend/src/modules/_reference/utils/index.js +3 -0
  59. package/template/frontend/src/shared/api/client.js +10 -0
  60. package/template/frontend/vite.config.js +6 -0
  61. package/template/package.json +16 -0
  62. package/template/scripts/lib/module-scaffold.mjs +409 -0
  63. package/template/scripts/new-module.mjs +58 -0
  64. package/template/scripts/run-module-evals.mjs +43 -0
  65. 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
+ }
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+ import { App } from "./core/App.jsx";
4
+ import "./index.css";
5
+
6
+ ReactDOM.createRoot(document.getElementById("root")).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>
10
+ );
File without changes
@@ -0,0 +1,3 @@
1
+ # Reference (example) (frontend)
2
+
3
+ See [Module internal contract](../../../docs/architecture/MODULE_INTERNAL_CONTRACT.md).
@@ -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,7 @@
1
+ import { _referencePage } from "./pages/_referencePage.jsx";
2
+
3
+ export default {
4
+ route: "/_reference",
5
+ label: "Reference (example)",
6
+ Component: _referencePage
7
+ };
@@ -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,3 @@
1
+ # UI prompts — Reference (example)
2
+
3
+ Optional: assistant copy, tool hints, and in-product AI instructions for this module.
@@ -0,0 +1,3 @@
1
+ export function isHealthResponse(value) {
2
+ return Boolean(value && typeof value.status === "string");
3
+ }
@@ -0,0 +1,5 @@
1
+ import { apiGet } from "../../shared/api/client.js";
2
+
3
+ export function fetchModuleHealth() {
4
+ return apiGet("/api/_reference/health");
5
+ }
@@ -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,3 @@
1
+ export function formatModuleLabel(label) {
2
+ return label?.trim() || "Module";
3
+ }
@@ -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,6 @@
1
+ import { defineConfig } from "vite";
2
+ import react from "@vitejs/plugin-react";
3
+
4
+ export default defineConfig({
5
+ plugins: [react()]
6
+ });
@@ -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
+ }