proteum 2.2.7 → 2.2.9
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/AGENTS.md +2 -1
- package/README.md +1 -1
- package/agents/project/AGENTS.md +4 -5
- package/agents/project/CODING_STYLE.md +5 -1
- package/agents/project/client/AGENTS.md +2 -0
- package/agents/project/diagnostics.md +2 -3
- package/agents/project/root/AGENTS.md +4 -5
- package/agents/project/tests/AGENTS.md +0 -1
- package/cli/commands/check.ts +21 -3
- package/cli/commands/configure.ts +1 -0
- package/cli/compiler/artifacts/controllers.ts +30 -4
- package/cli/compiler/artifacts/services.ts +67 -29
- package/cli/presentation/commands.ts +2 -2
- package/cli/scaffold/templates.ts +2 -3
- package/cli/utils/agents.ts +114 -4
- package/client/dev/profiler/ApexChart.tsx +4 -3
- package/common/dev/inspection.ts +16 -3
- package/common/dev/serverHotReload.ts +26 -25
- package/eslint.js +220 -0
- package/package.json +1 -1
- package/server/app/commands.ts +11 -16
- package/server/app/commandsManager.ts +5 -1
- package/server/app/controller/index.ts +68 -16
- package/server/app/devCommands.ts +3 -3
- package/server/app/devDiagnostics.ts +2 -2
- package/server/app/index.ts +19 -8
- package/server/app/service/container.ts +22 -19
- package/server/app/service/index.ts +33 -13
- package/server/app.tsconfig.json +0 -1
- package/server/services/auth/index.ts +12 -6
- package/server/services/auth/router/index.ts +12 -14
- package/server/services/auth/router/request.ts +34 -13
- package/server/services/disks/driver.ts +1 -1
- package/server/services/disks/index.ts +11 -8
- package/server/services/email/index.ts +1 -1
- package/server/services/prisma/Facet.ts +6 -5
- package/server/services/router/index.ts +8 -7
- package/server/services/router/request/validation/zod.ts +2 -0
- package/server/services/router/response/index.ts +9 -9
- package/server/services/router/service.ts +12 -8
- package/tests/agents-utils.test.cjs +55 -0
- package/tests/eslint-rules.test.cjs +110 -0
- package/types/global/vendors.d.ts +70 -0
|
@@ -159,12 +159,67 @@ test('monorepo configure writes root and app instruction files', () => {
|
|
|
159
159
|
fs.mkdirSync(path.join(monorepoRoot, '.git'));
|
|
160
160
|
fs.mkdirSync(path.join(appRoot, 'client'), { recursive: true });
|
|
161
161
|
|
|
162
|
+
configureProjectAgentInstructions({ appRoot, coreRoot });
|
|
163
|
+
|
|
162
164
|
const result = configureProjectAgentInstructions({ appRoot, coreRoot, monorepoRoot });
|
|
163
165
|
|
|
164
166
|
assert.equal(result.mode, 'monorepo');
|
|
165
167
|
assert.equal(resolveProjectAgentMonorepoRoot(appRoot), fs.realpathSync(monorepoRoot));
|
|
166
168
|
assert.match(fs.readFileSync(path.join(monorepoRoot, 'AGENTS.md'), 'utf8'), /## Source: AGENTS\.md/);
|
|
169
|
+
assert.match(fs.readFileSync(path.join(monorepoRoot, 'CODING_STYLE.md'), 'utf8'), /## Source: CODING_STYLE\.md/);
|
|
170
|
+
assert.match(fs.readFileSync(path.join(monorepoRoot, 'diagnostics.md'), 'utf8'), /## Source: AGENTS\.md/);
|
|
171
|
+
assert.match(fs.readFileSync(path.join(monorepoRoot, 'optimizations.md'), 'utf8'), /## Source: AGENTS\.md/);
|
|
167
172
|
assert.match(fs.readFileSync(path.join(appRoot, 'AGENTS.md'), 'utf8'), /## Source: client\/AGENTS\.md/);
|
|
173
|
+
assert.equal(fs.existsSync(path.join(appRoot, 'CODING_STYLE.md')), false);
|
|
174
|
+
assert.equal(fs.existsSync(path.join(appRoot, 'diagnostics.md')), false);
|
|
175
|
+
assert.equal(fs.existsSync(path.join(appRoot, 'optimizations.md')), false);
|
|
176
|
+
assert.equal(result.removed.some((entry) => entry.endsWith('/apps/product/CODING_STYLE.md')), true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('monorepo configure preserves local app-root documents', () => {
|
|
180
|
+
const coreRoot = createCoreFixture();
|
|
181
|
+
const monorepoRoot = makeTempRoot();
|
|
182
|
+
const appRoot = path.join(monorepoRoot, 'apps', 'product');
|
|
183
|
+
const localCodingStylePath = path.join(appRoot, 'CODING_STYLE.md');
|
|
184
|
+
|
|
185
|
+
fs.mkdirSync(path.join(monorepoRoot, '.git'));
|
|
186
|
+
writeFile(localCodingStylePath, '# Local Coding Style\n\n- Keep this app-local override.\n');
|
|
187
|
+
|
|
188
|
+
const result = configureProjectAgentInstructions({ appRoot, coreRoot, monorepoRoot });
|
|
189
|
+
|
|
190
|
+
assert.match(fs.readFileSync(path.join(monorepoRoot, 'CODING_STYLE.md'), 'utf8'), /## Source: CODING_STYLE\.md/);
|
|
191
|
+
assert.match(fs.readFileSync(localCodingStylePath, 'utf8'), /Keep this app-local override/);
|
|
192
|
+
assert.equal(result.removed.some((entry) => entry.endsWith('/apps/product/CODING_STYLE.md')), false);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test('monorepo configure strips retired managed sections from local app-root documents', () => {
|
|
196
|
+
const coreRoot = createCoreFixture();
|
|
197
|
+
const monorepoRoot = makeTempRoot();
|
|
198
|
+
const appRoot = path.join(monorepoRoot, 'apps', 'product');
|
|
199
|
+
const localCodingStylePath = path.join(appRoot, 'CODING_STYLE.md');
|
|
200
|
+
|
|
201
|
+
fs.mkdirSync(path.join(monorepoRoot, '.git'));
|
|
202
|
+
fs.mkdirSync(appRoot, { recursive: true });
|
|
203
|
+
configureProjectAgentInstructions({ appRoot, coreRoot });
|
|
204
|
+
|
|
205
|
+
const managedContent = fs.readFileSync(localCodingStylePath, 'utf8');
|
|
206
|
+
writeFile(
|
|
207
|
+
localCodingStylePath,
|
|
208
|
+
[
|
|
209
|
+
'# Local Coding Style',
|
|
210
|
+
'',
|
|
211
|
+
'- Keep this app-local override.',
|
|
212
|
+
'',
|
|
213
|
+
managedContent,
|
|
214
|
+
].join('\n'),
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const result = configureProjectAgentInstructions({ appRoot, coreRoot, monorepoRoot });
|
|
218
|
+
const retainedContent = fs.readFileSync(localCodingStylePath, 'utf8');
|
|
219
|
+
|
|
220
|
+
assert.match(retainedContent, /Keep this app-local override/);
|
|
221
|
+
assert.doesNotMatch(retainedContent, /proteum-instructions:start/);
|
|
222
|
+
assert.equal(result.updated.some((entry) => entry.endsWith('/apps/product/CODING_STYLE.md')), true);
|
|
168
223
|
});
|
|
169
224
|
|
|
170
225
|
test('configure migrates legacy managed symlinks to embedded files', () => {
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const assert = require('node:assert/strict');
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
const { Linter } = require('eslint');
|
|
4
|
+
|
|
5
|
+
const { createProteumEslintConfig } = require('../eslint.js');
|
|
6
|
+
|
|
7
|
+
const lint = (code) => {
|
|
8
|
+
const linter = new Linter({ configType: 'flat' });
|
|
9
|
+
return linter.verify(code, createProteumEslintConfig(), {
|
|
10
|
+
filename: 'client/example.tsx',
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const swallowedErrorRuleId = 'proteum/no-swallowed-caught-error';
|
|
15
|
+
|
|
16
|
+
test('proteum lint rejects empty catch blocks', () => {
|
|
17
|
+
const messages = lint(`
|
|
18
|
+
export const run = () => {
|
|
19
|
+
try {
|
|
20
|
+
risky();
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
`);
|
|
26
|
+
|
|
27
|
+
assert.equal(messages.some((message) => message.ruleId === swallowedErrorRuleId), true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('proteum lint rejects promise catches that discard the error', () => {
|
|
31
|
+
const messages = lint(`
|
|
32
|
+
export const run = () => {
|
|
33
|
+
api.load().catch(() => toast.error('Could not load'));
|
|
34
|
+
};
|
|
35
|
+
`);
|
|
36
|
+
|
|
37
|
+
assert.equal(messages.some((message) => message.ruleId === swallowedErrorRuleId), true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('proteum lint rejects generic catch feedback that drops original error details', () => {
|
|
41
|
+
const messages = lint(`
|
|
42
|
+
export const run = async () => {
|
|
43
|
+
try {
|
|
44
|
+
await Investor.api.getDashboard();
|
|
45
|
+
} catch (error) {
|
|
46
|
+
toast.error('Could not load API dashboard');
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
`);
|
|
50
|
+
|
|
51
|
+
assert.equal(messages.some((message) => message.ruleId === swallowedErrorRuleId), true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('proteum lint allows rethrowing the caught error', () => {
|
|
55
|
+
const messages = lint(`
|
|
56
|
+
export const run = async () => {
|
|
57
|
+
try {
|
|
58
|
+
await Investor.api.getDashboard();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
toast.error('Could not load API dashboard');
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
`);
|
|
65
|
+
|
|
66
|
+
assert.equal(messages.filter((message) => message.ruleId === swallowedErrorRuleId).length, 0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('proteum lint allows surfacing original error details', () => {
|
|
70
|
+
const messages = lint(`
|
|
71
|
+
export const run = async () => {
|
|
72
|
+
try {
|
|
73
|
+
await Investor.api.getDashboard();
|
|
74
|
+
} catch (error) {
|
|
75
|
+
toast.error('Could not load API dashboard', {
|
|
76
|
+
description: error instanceof Error ? error.message : String(error),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
`);
|
|
81
|
+
|
|
82
|
+
assert.equal(messages.filter((message) => message.ruleId === swallowedErrorRuleId).length, 0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('proteum lint allows surfacing a message derived from the caught error', () => {
|
|
86
|
+
const messages = lint(`
|
|
87
|
+
export const run = async () => {
|
|
88
|
+
try {
|
|
89
|
+
await Investor.api.getDashboard();
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
setError(message);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
`);
|
|
96
|
+
|
|
97
|
+
assert.equal(messages.filter((message) => message.ruleId === swallowedErrorRuleId).length, 0);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('proteum lint allows routing promise failures to app error handling', () => {
|
|
101
|
+
const messages = lint(`
|
|
102
|
+
export const run = () => {
|
|
103
|
+
Investor.api.ensureApiKey().catch((error) => {
|
|
104
|
+
Router.app.handleError(error);
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
`);
|
|
108
|
+
|
|
109
|
+
assert.equal(messages.filter((message) => message.ruleId === swallowedErrorRuleId).length, 0);
|
|
110
|
+
});
|
|
@@ -68,6 +68,76 @@ declare module 'morgan' {
|
|
|
68
68
|
export default morgan;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
declare module 'object-hash' {
|
|
72
|
+
export type ObjectHashOptions = {
|
|
73
|
+
unorderedArrays?: boolean;
|
|
74
|
+
unorderedObjects?: boolean;
|
|
75
|
+
};
|
|
76
|
+
export type ObjectHashValue =
|
|
77
|
+
| string
|
|
78
|
+
| number
|
|
79
|
+
| boolean
|
|
80
|
+
| null
|
|
81
|
+
| undefined
|
|
82
|
+
| Date
|
|
83
|
+
| ObjectHashValue[]
|
|
84
|
+
| { [key: string]: ObjectHashValue };
|
|
85
|
+
|
|
86
|
+
const objectHash: (value: ObjectHashValue, options?: ObjectHashOptions) => string;
|
|
87
|
+
export default objectHash;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
declare module 'fs-extra' {
|
|
91
|
+
type FsJsonValue =
|
|
92
|
+
| string
|
|
93
|
+
| number
|
|
94
|
+
| boolean
|
|
95
|
+
| null
|
|
96
|
+
| FsJsonValue[]
|
|
97
|
+
| { [key: string]: FsJsonValue };
|
|
98
|
+
type FsExtraModule = {
|
|
99
|
+
createReadStream: typeof import('fs').createReadStream;
|
|
100
|
+
ensureDirSync(path: import('fs').PathLike): void;
|
|
101
|
+
existsSync: typeof import('fs').existsSync;
|
|
102
|
+
moveSync(source: import('fs').PathLike, destination: import('fs').PathLike, options?: { overwrite?: boolean }): void;
|
|
103
|
+
outputFileSync(
|
|
104
|
+
file: import('fs').PathOrFileDescriptor,
|
|
105
|
+
data: string | NodeJS.ArrayBufferView,
|
|
106
|
+
options?: import('fs').WriteFileOptions | { encoding?: string },
|
|
107
|
+
): void;
|
|
108
|
+
readdir: typeof import('fs/promises').readdir;
|
|
109
|
+
readdirSync: typeof import('fs').readdirSync;
|
|
110
|
+
readFile: typeof import('fs/promises').readFile;
|
|
111
|
+
readFileSync: typeof import('fs').readFileSync;
|
|
112
|
+
readJSONSync<TValue = FsJsonValue>(path: import('fs').PathLike): TValue;
|
|
113
|
+
readJsonSync<TValue = FsJsonValue>(path: import('fs').PathLike): TValue;
|
|
114
|
+
removeSync(path: import('fs').PathLike): void;
|
|
115
|
+
statSync: typeof import('fs').statSync;
|
|
116
|
+
writeJSONSync(path: import('fs').PathLike, data: FsJsonValue | object, options?: Record<string, unknown>): void;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const fsExtra: FsExtraModule;
|
|
120
|
+
export default fsExtra;
|
|
121
|
+
export const createReadStream: FsExtraModule['createReadStream'];
|
|
122
|
+
export const ensureDirSync: FsExtraModule['ensureDirSync'];
|
|
123
|
+
export const existsSync: FsExtraModule['existsSync'];
|
|
124
|
+
export const moveSync: FsExtraModule['moveSync'];
|
|
125
|
+
export const outputFileSync: FsExtraModule['outputFileSync'];
|
|
126
|
+
export const readdir: typeof import('fs/promises').readdir;
|
|
127
|
+
export const readdirSync: FsExtraModule['readdirSync'];
|
|
128
|
+
export const readFile: typeof import('fs/promises').readFile;
|
|
129
|
+
export const readFileSync: FsExtraModule['readFileSync'];
|
|
130
|
+
export const readJSONSync: FsExtraModule['readJSONSync'];
|
|
131
|
+
export const readJsonSync: FsExtraModule['readJsonSync'];
|
|
132
|
+
export const removeSync: FsExtraModule['removeSync'];
|
|
133
|
+
export const statSync: FsExtraModule['statSync'];
|
|
134
|
+
export const writeJSONSync: FsExtraModule['writeJSONSync'];
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
declare module 'jstoxml' {
|
|
138
|
+
export const toXML: (value: Record<string, unknown>, options?: Record<string, unknown>) => string;
|
|
139
|
+
}
|
|
140
|
+
|
|
71
141
|
declare module 'stopword' {
|
|
72
142
|
export const eng: string[];
|
|
73
143
|
export function removeStopwords(words: string[], stopwords?: string[]): string[];
|