proteum 2.1.3-1 → 2.1.7
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 +22 -14
- package/README.md +109 -17
- package/agents/project/AGENTS.md +188 -25
- package/agents/project/CODING_STYLE.md +1 -0
- package/agents/project/client/AGENTS.md +13 -8
- package/agents/project/client/pages/AGENTS.md +17 -9
- package/agents/project/diagnostics.md +52 -0
- package/agents/project/optimizations.md +48 -0
- package/agents/project/server/routes/AGENTS.md +9 -6
- package/agents/project/server/services/AGENTS.md +10 -6
- package/agents/project/tests/AGENTS.md +11 -5
- package/cli/app/config.ts +13 -14
- package/cli/app/index.ts +58 -0
- package/cli/commands/connect.ts +45 -0
- package/cli/commands/dev.ts +37 -13
- package/cli/commands/diagnose.ts +286 -0
- package/cli/commands/doctor.ts +18 -5
- package/cli/commands/explain.ts +25 -0
- package/cli/commands/perf.ts +243 -0
- package/cli/commands/trace.ts +9 -1
- package/cli/commands/verify.ts +281 -0
- package/cli/compiler/artifacts/connectedProjects.ts +453 -0
- package/cli/compiler/artifacts/controllers.ts +198 -49
- package/cli/compiler/artifacts/discovery.ts +0 -34
- package/cli/compiler/artifacts/manifest.ts +95 -6
- package/cli/compiler/artifacts/routing.ts +2 -2
- package/cli/compiler/artifacts/services.ts +277 -130
- package/cli/compiler/client/index.ts +3 -0
- package/cli/compiler/common/files/style.ts +52 -0
- package/cli/compiler/common/generatedRouteModules.ts +34 -5
- package/cli/compiler/common/scripts.ts +11 -5
- package/cli/compiler/index.ts +2 -1
- package/cli/compiler/server/index.ts +3 -0
- package/cli/presentation/commands.ts +110 -7
- package/cli/presentation/devSession.ts +32 -7
- package/cli/runtime/commands.ts +165 -6
- package/cli/scaffold/index.ts +18 -27
- package/cli/scaffold/templates.ts +48 -28
- package/cli/utils/agents.ts +106 -13
- package/cli/utils/keyboard.ts +8 -0
- package/client/dev/profiler/ApexChart.tsx +66 -0
- package/client/dev/profiler/index.tsx +2508 -302
- package/client/dev/profiler/runtime.noop.ts +12 -0
- package/client/dev/profiler/runtime.ts +195 -4
- package/client/services/router/request/api.ts +6 -1
- package/common/applicationConfig.ts +173 -0
- package/common/applicationConfigLoader.ts +102 -0
- package/common/connectedProjects.ts +113 -0
- package/common/dev/connect.ts +267 -0
- package/common/dev/console.ts +31 -0
- package/common/dev/contractsDoctor.ts +128 -0
- package/common/dev/diagnostics.ts +59 -15
- package/common/dev/inspection.ts +491 -0
- package/common/dev/performance.ts +809 -0
- package/common/dev/profiler.ts +3 -0
- package/common/dev/proteumManifest.ts +31 -6
- package/common/dev/requestTrace.ts +52 -1
- package/common/env/proteumEnv.ts +176 -50
- package/common/router/index.ts +1 -0
- package/common/router/request/api.ts +2 -0
- package/config.ts +5 -0
- package/docs/dev-commands.md +5 -1
- package/docs/dev-sessions.md +90 -0
- package/docs/diagnostics.md +74 -11
- package/docs/request-tracing.md +50 -3
- package/package.json +1 -1
- package/server/app/container/config.ts +16 -87
- package/server/app/container/console/index.ts +42 -8
- package/server/app/container/index.ts +10 -2
- package/server/app/container/trace/index.ts +105 -0
- package/server/app/devDiagnostics.ts +138 -0
- package/server/app/index.ts +18 -8
- package/server/app/service/container.ts +0 -12
- package/server/app/service/index.ts +0 -2
- package/server/services/prisma/index.ts +121 -4
- package/server/services/router/http/index.ts +305 -11
- package/server/services/router/index.ts +116 -57
- package/server/services/router/request/api.ts +160 -19
- package/server/services/router/request/index.ts +8 -0
- package/server/services/router/response/index.ts +23 -1
- package/server/services/router/response/page/document.tsx +31 -14
- package/server/services/router/response/page/index.tsx +10 -0
- package/agents/framework/AGENTS.md +0 -177
- package/server/services/auth/router/service.json +0 -6
- package/server/services/auth/service.json +0 -6
- package/server/services/cron/service.json +0 -6
- package/server/services/disks/drivers/local/service.json +0 -6
- package/server/services/disks/drivers/s3/service.json +0 -6
- package/server/services/disks/service.json +0 -6
- package/server/services/fetch/service.json +0 -7
- package/server/services/prisma/service.json +0 -6
- package/server/services/router/service.json +0 -6
- package/server/services/schema/router/service.json +0 -6
- package/server/services/schema/service.json +0 -6
- package/server/services/security/encrypt/aes/service.json +0 -6
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
import got from 'got';
|
|
6
|
+
import { UsageError } from 'clipanion';
|
|
7
|
+
|
|
8
|
+
import cli from '..';
|
|
9
|
+
|
|
10
|
+
type TVerifyAppResult = {
|
|
11
|
+
appRoot: string;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
contracts: { errors: number; warnings: number };
|
|
14
|
+
doctor: { errors: number; warnings: number };
|
|
15
|
+
explain: { commands: number; controllers: number; routes: number };
|
|
16
|
+
name: string;
|
|
17
|
+
page: { statusCode: number; url: string };
|
|
18
|
+
startup: 'reused' | 'spawned';
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
type TVerifyResult = {
|
|
22
|
+
action: string;
|
|
23
|
+
apps: TVerifyAppResult[];
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
type TEnsureServerResult =
|
|
27
|
+
| { baseUrl: string; startup: 'reused' }
|
|
28
|
+
| { baseUrl: string; close: () => void; startup: 'spawned' };
|
|
29
|
+
|
|
30
|
+
type TVerifyAppConfig = {
|
|
31
|
+
appRoot: string;
|
|
32
|
+
envOverrides?: Record<string, string>;
|
|
33
|
+
name: string;
|
|
34
|
+
port: number;
|
|
35
|
+
route: string;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const defaultApps = {
|
|
39
|
+
crosspath: '/Users/gaetan/Desktop/Projets/crosspath/platform',
|
|
40
|
+
product: '/Users/gaetan/Desktop/Projets/unique.domains/product',
|
|
41
|
+
website: '/Users/gaetan/Desktop/Projets/unique.domains/website',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const normalizeBaseUrl = (value: string) => value.replace(/\/+$/, '');
|
|
45
|
+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
46
|
+
const dedupe = <TValue>(values: TValue[]) => [...new Set(values)];
|
|
47
|
+
const createLocalBaseUrl = (port: number) => `http://localhost:${port}`;
|
|
48
|
+
const getBaseUrlCandidates = (port: number) =>
|
|
49
|
+
dedupe([createLocalBaseUrl(port), `http://127.0.0.1:${port}`, `http://[::1]:${port}`]);
|
|
50
|
+
|
|
51
|
+
const fetchJson = async <TResponse>(baseUrl: string, pathname: string) => {
|
|
52
|
+
const response = await got(`${normalizeBaseUrl(baseUrl)}${pathname}`, {
|
|
53
|
+
responseType: 'json',
|
|
54
|
+
retry: { limit: 0 },
|
|
55
|
+
throwHttpErrors: false,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (response.statusCode >= 400) throw new UsageError(`Request ${pathname} failed with status ${response.statusCode}.`);
|
|
59
|
+
return response.body as TResponse;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const waitForServer = async (baseUrls: string[], timeoutMs = 120000) => {
|
|
63
|
+
const startedAt = Date.now();
|
|
64
|
+
let lastError: string | undefined;
|
|
65
|
+
|
|
66
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
67
|
+
for (const baseUrl of baseUrls) {
|
|
68
|
+
try {
|
|
69
|
+
await fetchJson(baseUrl, '/__proteum/explain?section=app');
|
|
70
|
+
return baseUrl;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
await sleep(1000);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
throw new UsageError(
|
|
80
|
+
`Timed out while waiting for ${baseUrls.join(', ')} to expose Proteum dev diagnostics.${lastError ? ` Last error: ${lastError}` : ''}`,
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const ensureServer = async ({
|
|
85
|
+
appRoot,
|
|
86
|
+
envOverrides,
|
|
87
|
+
port,
|
|
88
|
+
}: {
|
|
89
|
+
appRoot: string;
|
|
90
|
+
envOverrides?: Record<string, string>;
|
|
91
|
+
port: number;
|
|
92
|
+
}): Promise<TEnsureServerResult> => {
|
|
93
|
+
const baseUrls = getBaseUrlCandidates(port);
|
|
94
|
+
|
|
95
|
+
for (const baseUrl of baseUrls) {
|
|
96
|
+
try {
|
|
97
|
+
await fetchJson(baseUrl, '/__proteum/explain?section=app');
|
|
98
|
+
return { baseUrl, startup: 'reused' as const };
|
|
99
|
+
} catch (_error) {}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const desiredBaseUrl = createLocalBaseUrl(port);
|
|
103
|
+
const cliBin = path.join(cli.paths.core.root, 'cli', 'bin.js');
|
|
104
|
+
const child = spawn(process.execPath, [cliBin, 'dev', '--no-cache', '--port', String(port)], {
|
|
105
|
+
cwd: appRoot,
|
|
106
|
+
env: {
|
|
107
|
+
...process.env,
|
|
108
|
+
PORT: String(port),
|
|
109
|
+
URL: desiredBaseUrl,
|
|
110
|
+
URL_INTERNAL: desiredBaseUrl,
|
|
111
|
+
...(envOverrides || {}),
|
|
112
|
+
},
|
|
113
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const close = () => {
|
|
117
|
+
if (!child.killed) child.kill('SIGTERM');
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const baseUrl = await waitForServer(baseUrls);
|
|
122
|
+
return { baseUrl, close, startup: 'spawned' as const };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
close();
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const collectAppResult = async ({
|
|
130
|
+
appRoot,
|
|
131
|
+
baseUrl,
|
|
132
|
+
name,
|
|
133
|
+
route,
|
|
134
|
+
startup,
|
|
135
|
+
}: {
|
|
136
|
+
appRoot: string;
|
|
137
|
+
baseUrl: string;
|
|
138
|
+
name: string;
|
|
139
|
+
route: string;
|
|
140
|
+
startup: 'reused' | 'spawned';
|
|
141
|
+
}): Promise<TVerifyAppResult> => {
|
|
142
|
+
const explain = await fetchJson<{
|
|
143
|
+
controllers?: unknown[];
|
|
144
|
+
routes?: { client?: unknown[]; server?: unknown[] };
|
|
145
|
+
commands?: unknown[];
|
|
146
|
+
}>(baseUrl, '/__proteum/explain');
|
|
147
|
+
const doctor = await fetchJson<{ summary: { errors: number; warnings: number } }>(baseUrl, '/__proteum/doctor');
|
|
148
|
+
const contracts = await fetchJson<{ summary: { errors: number; warnings: number } }>(baseUrl, '/__proteum/doctor/contracts');
|
|
149
|
+
const pageResponse = await got(`${baseUrl}${route}`, {
|
|
150
|
+
followRedirect: false,
|
|
151
|
+
retry: { limit: 0 },
|
|
152
|
+
throwHttpErrors: false,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
appRoot,
|
|
157
|
+
baseUrl,
|
|
158
|
+
contracts: contracts.summary,
|
|
159
|
+
doctor: doctor.summary,
|
|
160
|
+
explain: {
|
|
161
|
+
commands: Array.isArray(explain.commands) ? explain.commands.length : 0,
|
|
162
|
+
controllers: Array.isArray(explain.controllers) ? explain.controllers.length : 0,
|
|
163
|
+
routes:
|
|
164
|
+
(Array.isArray(explain.routes?.client) ? explain.routes.client.length : 0) +
|
|
165
|
+
(Array.isArray(explain.routes?.server) ? explain.routes.server.length : 0),
|
|
166
|
+
},
|
|
167
|
+
name,
|
|
168
|
+
page: { statusCode: pageResponse.statusCode, url: `${baseUrl}${route}` },
|
|
169
|
+
startup,
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const renderHuman = (result: TVerifyResult) =>
|
|
174
|
+
[
|
|
175
|
+
`Proteum verify ${result.action}`,
|
|
176
|
+
...result.apps.flatMap((app) => [
|
|
177
|
+
'',
|
|
178
|
+
`${app.name}`,
|
|
179
|
+
`- root=${app.appRoot}`,
|
|
180
|
+
`- baseUrl=${app.baseUrl}`,
|
|
181
|
+
`- startup=${app.startup}`,
|
|
182
|
+
`- page=${app.page.statusCode} ${app.page.url}`,
|
|
183
|
+
`- explain routes=${app.explain.routes} controllers=${app.explain.controllers} commands=${app.explain.commands}`,
|
|
184
|
+
`- doctor errors=${app.doctor.errors} warnings=${app.doctor.warnings}`,
|
|
185
|
+
`- contracts errors=${app.contracts.errors} warnings=${app.contracts.warnings}`,
|
|
186
|
+
]),
|
|
187
|
+
].join('\n');
|
|
188
|
+
|
|
189
|
+
export const run = async () => {
|
|
190
|
+
const action = typeof cli.args.action === 'string' && cli.args.action ? cli.args.action : 'framework-change';
|
|
191
|
+
if (action !== 'framework-change') throw new UsageError(`Unsupported verify action "${action}".`);
|
|
192
|
+
|
|
193
|
+
const websiteRoute = typeof cli.args.route === 'string' && cli.args.route ? cli.args.route : '/';
|
|
194
|
+
const apps = {
|
|
195
|
+
crosspath: {
|
|
196
|
+
appRoot: (typeof cli.args.crosspath === 'string' && cli.args.crosspath) || defaultApps.crosspath,
|
|
197
|
+
name: 'CrossPath',
|
|
198
|
+
port: Number((typeof cli.args.crosspathPort === 'string' && cli.args.crosspathPort) || 3011),
|
|
199
|
+
route: '/',
|
|
200
|
+
} satisfies TVerifyAppConfig,
|
|
201
|
+
product: {
|
|
202
|
+
appRoot: (typeof cli.args.product === 'string' && cli.args.product) || defaultApps.product,
|
|
203
|
+
name: 'Unique Domains Product',
|
|
204
|
+
port: Number((typeof cli.args.productPort === 'string' && cli.args.productPort) || 3021),
|
|
205
|
+
route: '/',
|
|
206
|
+
} satisfies TVerifyAppConfig,
|
|
207
|
+
website: {
|
|
208
|
+
appRoot: (typeof cli.args.website === 'string' && cli.args.website) || defaultApps.website,
|
|
209
|
+
name: 'Unique Domains Website',
|
|
210
|
+
port: Number((typeof cli.args.websitePort === 'string' && cli.args.websitePort) || 3031),
|
|
211
|
+
route: websiteRoute,
|
|
212
|
+
} satisfies TVerifyAppConfig,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
for (const app of Object.values(apps)) {
|
|
216
|
+
if (!fs.existsSync(app.appRoot)) {
|
|
217
|
+
throw new UsageError(`Reference app "${app.name}" was not found at ${app.appRoot}.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const startedServers: Array<() => void> = [];
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const results: TVerifyAppResult[] = [];
|
|
225
|
+
|
|
226
|
+
const productServer = await ensureServer({
|
|
227
|
+
appRoot: apps.product.appRoot,
|
|
228
|
+
port: apps.product.port,
|
|
229
|
+
});
|
|
230
|
+
if (productServer.startup === 'spawned') startedServers.push(productServer.close);
|
|
231
|
+
|
|
232
|
+
const websiteServer = await ensureServer({
|
|
233
|
+
appRoot: apps.website.appRoot,
|
|
234
|
+
envOverrides: {
|
|
235
|
+
PRODUCT_CONNECTED_SOURCE: `file:${apps.product.appRoot}`,
|
|
236
|
+
PRODUCT_URL_INTERNAL: productServer.baseUrl,
|
|
237
|
+
},
|
|
238
|
+
port: apps.website.port,
|
|
239
|
+
});
|
|
240
|
+
if (websiteServer.startup === 'spawned') startedServers.push(websiteServer.close);
|
|
241
|
+
|
|
242
|
+
const crosspathServer = await ensureServer({
|
|
243
|
+
appRoot: apps.crosspath.appRoot,
|
|
244
|
+
port: apps.crosspath.port,
|
|
245
|
+
});
|
|
246
|
+
if (crosspathServer.startup === 'spawned') startedServers.push(crosspathServer.close);
|
|
247
|
+
|
|
248
|
+
results.push(
|
|
249
|
+
await collectAppResult({
|
|
250
|
+
...apps.crosspath,
|
|
251
|
+
baseUrl: crosspathServer.baseUrl,
|
|
252
|
+
startup: crosspathServer.startup,
|
|
253
|
+
}),
|
|
254
|
+
);
|
|
255
|
+
results.push(
|
|
256
|
+
await collectAppResult({
|
|
257
|
+
...apps.product,
|
|
258
|
+
baseUrl: productServer.baseUrl,
|
|
259
|
+
startup: productServer.startup,
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
results.push(
|
|
263
|
+
await collectAppResult({
|
|
264
|
+
...apps.website,
|
|
265
|
+
baseUrl: websiteServer.baseUrl,
|
|
266
|
+
startup: websiteServer.startup,
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const result = { action, apps: results } satisfies TVerifyResult;
|
|
271
|
+
|
|
272
|
+
if (cli.args.json === true) {
|
|
273
|
+
console.log(JSON.stringify(result, null, 2));
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
console.log(renderHuman(result));
|
|
278
|
+
} finally {
|
|
279
|
+
for (const close of startedServers.reverse()) close();
|
|
280
|
+
}
|
|
281
|
+
};
|