proteum 2.5.0 → 2.5.2

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 (40) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +46 -19
  3. package/agents/project/AGENTS.md +9 -7
  4. package/agents/project/CODING_STYLE.md +1 -1
  5. package/agents/project/client/AGENTS.md +5 -1
  6. package/agents/project/diagnostics.md +1 -1
  7. package/agents/project/root/AGENTS.md +9 -7
  8. package/agents/project/server/services/AGENTS.md +4 -0
  9. package/agents/project/tests/AGENTS.md +1 -1
  10. package/cli/commands/verify.ts +117 -4
  11. package/cli/compiler/artifacts/controllerHelper.ts +66 -0
  12. package/cli/compiler/artifacts/controllers.ts +3 -0
  13. package/cli/compiler/artifacts/services.ts +14 -8
  14. package/cli/compiler/common/generatedRouteModules.ts +270 -53
  15. package/cli/presentation/commands.ts +11 -1
  16. package/cli/runtime/commands.ts +6 -0
  17. package/cli/scaffold/templates.ts +14 -6
  18. package/cli/utils/agents.ts +1 -1
  19. package/cli/verification/changed.ts +460 -0
  20. package/client/app/index.ts +22 -5
  21. package/client/services/router/index.tsx +1 -1
  22. package/client/services/router/request/api.ts +2 -2
  23. package/common/applicationConfig.ts +177 -0
  24. package/common/applicationConfigLoader.ts +33 -1
  25. package/common/dev/contractsDoctor.ts +16 -0
  26. package/config.ts +5 -1
  27. package/docs/migration-2.5.md +269 -0
  28. package/eslint.js +96 -50
  29. package/package.json +1 -1
  30. package/server/app/index.ts +28 -2
  31. package/server/services/router/index.ts +3 -3
  32. package/tests/cli-mcp-command.test.cjs +14 -0
  33. package/tests/client-app-error-handling.test.cjs +100 -0
  34. package/tests/contracts-doctor.test.cjs +98 -0
  35. package/tests/definition-contracts.test.cjs +129 -0
  36. package/tests/dev-transpile-watch.test.cjs +3 -6
  37. package/tests/eslint-rules.test.cjs +246 -7
  38. package/tests/scaffold-templates.test.cjs +43 -0
  39. package/tests/server-app-report-error.test.cjs +135 -0
  40. package/tests/verify-changed.test.cjs +200 -0
@@ -0,0 +1,200 @@
1
+ const assert = require('node:assert/strict');
2
+ const { spawnSync } = require('node:child_process');
3
+ const fs = require('node:fs');
4
+ const os = require('node:os');
5
+ const path = require('node:path');
6
+
7
+ const coreRoot = path.resolve(__dirname, '..');
8
+ const cliBin = path.join(coreRoot, 'cli', 'bin.js');
9
+
10
+ process.env.TS_NODE_PROJECT = path.join(coreRoot, 'cli', 'tsconfig.json');
11
+ process.env.TS_NODE_TRANSPILE_ONLY = '1';
12
+ require('ts-node/register/transpile-only');
13
+
14
+ const { buildChangedVerificationPlan, runChangedVerification } = require('../cli/verification/changed.ts');
15
+
16
+ const createRoot = () => fs.mkdtempSync(path.join(os.tmpdir(), 'proteum-verify-changed-'));
17
+
18
+ const writeFile = (root, filepath, content = '') => {
19
+ const fullPath = path.join(root, filepath);
20
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
21
+ fs.writeFileSync(fullPath, content);
22
+ };
23
+
24
+ const planCommands = (plan) => plan.selectedChecks.map((check) => check.command);
25
+ const planIds = (plan) => plan.selectedChecks.map((check) => check.id);
26
+
27
+ test('changed verification planner runs changed test files directly', () => {
28
+ const root = createRoot();
29
+ writeFile(root, 'tests/unit/session.test.ts', 'test("session", () => {});\n');
30
+
31
+ const plan = buildChangedVerificationPlan({
32
+ cwd: root,
33
+ changedFiles: ['tests/unit/session.test.ts'],
34
+ });
35
+
36
+ assert.deepEqual(planCommands(plan), ["npx vitest run 'tests/unit/session.test.ts'"]);
37
+ assert.deepEqual(plan.selectedChecks[0].matchedFiles, ['tests/unit/session.test.ts']);
38
+ });
39
+
40
+ test('changed verification planner runs related tests for source files', () => {
41
+ const root = createRoot();
42
+ writeFile(root, 'packages/auth/src/session.ts', 'export const session = true;\n');
43
+
44
+ const plan = buildChangedVerificationPlan({
45
+ cwd: root,
46
+ changedFiles: ['packages/auth/src/session.ts'],
47
+ });
48
+
49
+ assert.deepEqual(planCommands(plan), ["npx vitest related 'packages/auth/src/session.ts'"]);
50
+ assert.deepEqual(plan.selectedChecks[0].matchedFiles, ['packages/auth/src/session.ts']);
51
+ });
52
+
53
+ test('changed verification planner loads project config and applies Klair-style MCP rules', () => {
54
+ const root = createRoot();
55
+ writeFile(
56
+ root,
57
+ 'proteum.verify.config.ts',
58
+ `import { defineVerificationConfig } from 'proteum/config';
59
+
60
+ export default defineVerificationConfig({
61
+ suites: {
62
+ mcpFast: 'npm run test:mcp:fast',
63
+ mcpBranches: 'npm run test:mcp:branches',
64
+ },
65
+ rules: [
66
+ {
67
+ id: 'mcp-fast',
68
+ match: ['packages/mcp/src/**', 'packages/2_usecases/src/mcp-v7/**'],
69
+ run: ['mcpFast'],
70
+ reason: 'MCP behavior changed.',
71
+ },
72
+ {
73
+ id: 'mcp-branches',
74
+ match: [
75
+ 'packages/mcp/src/**/catalog*.ts',
76
+ 'packages/mcp/src/**/dispatcher*.ts',
77
+ 'packages/mcp/src/**/fallback*.ts',
78
+ 'packages/mcp/src/**/validation*.ts',
79
+ 'packages/2_usecases/src/mcp-v7/**/catalog*.ts',
80
+ 'packages/2_usecases/src/mcp-v7/**/dispatcher*.ts',
81
+ 'packages/2_usecases/src/mcp-v7/**/fallback*.ts',
82
+ 'packages/2_usecases/src/mcp-v7/**/validation*.ts',
83
+ ],
84
+ run: ['mcpBranches'],
85
+ reason: 'MCP branch-risk behavior changed.',
86
+ },
87
+ ],
88
+ });
89
+ `,
90
+ );
91
+ writeFile(root, 'packages/mcp/src/server.ts', 'export const server = true;\n');
92
+ writeFile(root, 'packages/mcp/src/dispatcher.ts', 'export const dispatcher = true;\n');
93
+
94
+ const normalPlan = buildChangedVerificationPlan({
95
+ cwd: root,
96
+ changedFiles: ['packages/mcp/src/server.ts'],
97
+ });
98
+ assert.ok(planCommands(normalPlan).includes('npm run test:mcp:fast'));
99
+ assert.equal(planCommands(normalPlan).includes('npm run test:mcp:branches'), false);
100
+
101
+ const branchRiskPlan = buildChangedVerificationPlan({
102
+ cwd: root,
103
+ changedFiles: ['packages/mcp/src/dispatcher.ts'],
104
+ });
105
+ assert.ok(planCommands(branchRiskPlan).includes('npm run test:mcp:fast'));
106
+ assert.ok(planCommands(branchRiskPlan).includes('npm run test:mcp:branches'));
107
+ });
108
+
109
+ test('changed verification planner skips tests for docs-only changes', () => {
110
+ const root = createRoot();
111
+ writeFile(root, 'docs/testing.md', '# Testing\n');
112
+
113
+ const plan = buildChangedVerificationPlan({
114
+ cwd: root,
115
+ changedFiles: ['docs/testing.md'],
116
+ });
117
+
118
+ assert.deepEqual(plan.selectedChecks, []);
119
+ assert.equal(plan.docsOnly, true);
120
+ assert.deepEqual(plan.skippedChecks.map((check) => check.id), ['builtin:docs-only']);
121
+ });
122
+
123
+ test('changed verification dry-run reports the plan without executing checks', async () => {
124
+ const root = createRoot();
125
+ writeFile(
126
+ root,
127
+ 'proteum.verify.config.ts',
128
+ `import { defineVerificationConfig } from 'proteum/config';
129
+
130
+ export default defineVerificationConfig({
131
+ suites: {
132
+ fail: 'node -e "process.exit(7)"',
133
+ },
134
+ always: ['fail'],
135
+ });
136
+ `,
137
+ );
138
+ writeFile(root, 'README.md', '# Docs\n');
139
+
140
+ const result = await runChangedVerification({
141
+ cwd: root,
142
+ changedFiles: ['README.md'],
143
+ dryRun: true,
144
+ });
145
+
146
+ assert.equal(result.dryRun, true);
147
+ assert.equal(result.executions.length, 0);
148
+ assert.equal(result.result.ok, true);
149
+ assert.ok(planIds(result).includes('always:fail'));
150
+ });
151
+
152
+ test('changed verification captures command failures and returns a failed result', async () => {
153
+ const root = createRoot();
154
+ writeFile(
155
+ root,
156
+ 'proteum.verify.config.ts',
157
+ `import { defineVerificationConfig } from 'proteum/config';
158
+
159
+ export default defineVerificationConfig({
160
+ suites: {
161
+ fail: 'node -e "process.exit(7)"',
162
+ },
163
+ always: ['fail'],
164
+ });
165
+ `,
166
+ );
167
+ writeFile(root, 'README.md', '# Docs\n');
168
+
169
+ const result = await runChangedVerification({
170
+ cwd: root,
171
+ changedFiles: ['README.md'],
172
+ });
173
+
174
+ assert.equal(result.result.ok, false);
175
+ assert.equal(result.result.failedChecks, 1);
176
+ assert.deepEqual(result.executions.map((execution) => execution.status), ['failed']);
177
+ assert.equal(result.executions[0].exitCode, 7);
178
+ });
179
+
180
+ test('verify changed CLI JSON output keeps the planner and execution shape stable', () => {
181
+ const root = createRoot();
182
+ spawnSync('git', ['init'], { cwd: root, encoding: 'utf8' });
183
+ writeFile(root, 'docs/testing.md', '# Testing\n');
184
+
185
+ const result = spawnSync(process.execPath, [cliBin, 'verify', 'changed', '--dry-run', '--json'], {
186
+ cwd: root,
187
+ encoding: 'utf8',
188
+ });
189
+
190
+ assert.equal(result.status, 0, result.stderr);
191
+ const output = JSON.parse(result.stdout);
192
+
193
+ assert.deepEqual(output.changedFiles, ['docs/testing.md']);
194
+ assert.ok(Array.isArray(output.selectedChecks));
195
+ assert.ok(Array.isArray(output.skippedChecks));
196
+ assert.ok(Array.isArray(output.executions));
197
+ assert.equal(typeof output.result.ok, 'boolean');
198
+ assert.equal(output.result.selectedChecks, 0);
199
+ assert.equal(output.result.failedChecks, 0);
200
+ });