proteum 2.1.0 → 2.1.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.
- package/AGENTS.md +44 -98
- package/README.md +143 -10
- package/agents/framework/AGENTS.md +146 -886
- package/agents/project/AGENTS.md +73 -127
- package/agents/project/client/AGENTS.md +22 -93
- package/agents/project/client/pages/AGENTS.md +24 -26
- package/agents/project/server/routes/AGENTS.md +10 -8
- package/agents/project/server/services/AGENTS.md +22 -159
- package/agents/project/tests/AGENTS.md +11 -8
- package/cli/app/config.ts +7 -20
- package/cli/bin.js +8 -0
- package/cli/commands/command.ts +243 -0
- package/cli/commands/commandLocalRunner.js +198 -0
- package/cli/commands/create.ts +5 -0
- package/cli/commands/deploy/web.ts +1 -2
- package/cli/commands/dev.ts +98 -2
- package/cli/commands/doctor.ts +8 -74
- package/cli/commands/explain.ts +8 -186
- package/cli/commands/init.ts +2 -94
- package/cli/commands/trace.ts +228 -0
- package/cli/compiler/artifacts/commands.ts +217 -0
- package/cli/compiler/artifacts/manifest.ts +35 -21
- package/cli/compiler/artifacts/services.ts +300 -1
- package/cli/compiler/client/index.ts +43 -8
- package/cli/compiler/common/commands.ts +175 -0
- package/cli/compiler/common/index.ts +1 -1
- package/cli/compiler/common/proteumManifest.ts +15 -114
- package/cli/compiler/index.ts +25 -2
- package/cli/compiler/server/index.ts +31 -6
- package/cli/index.ts +1 -4
- package/cli/paths.ts +16 -1
- package/cli/presentation/commands.ts +104 -14
- package/cli/presentation/devSession.ts +22 -3
- package/cli/presentation/proteum_logo_400x400_square_icon.txt +400 -0
- package/cli/runtime/commands.ts +121 -4
- package/cli/scaffold/index.ts +720 -0
- package/cli/scaffold/templates.ts +344 -0
- package/cli/scaffold/types.ts +26 -0
- package/cli/tsconfig.json +4 -1
- package/cli/utils/check.ts +1 -1
- package/client/app/component.tsx +13 -9
- package/client/dev/profiler/index.tsx +2511 -0
- package/client/dev/profiler/noop.tsx +5 -0
- package/client/dev/profiler/runtime.noop.ts +116 -0
- package/client/dev/profiler/runtime.ts +840 -0
- package/client/services/router/components/router.tsx +30 -2
- package/client/services/router/index.tsx +27 -3
- package/client/services/router/request/api.ts +133 -17
- package/commands/proteum/diagnostics.ts +11 -0
- package/common/dev/commands.ts +50 -0
- package/common/dev/diagnostics.ts +298 -0
- package/common/dev/profiler.ts +92 -0
- package/common/dev/proteumManifest.ts +135 -0
- package/common/dev/requestTrace.ts +115 -0
- package/common/env/proteumEnv.ts +284 -0
- package/common/router/index.ts +4 -22
- package/docs/dev-commands.md +93 -0
- package/docs/diagnostics.md +88 -0
- package/docs/request-tracing.md +132 -0
- package/eslint.js +11 -6
- package/package.json +3 -3
- package/server/app/commands.ts +35 -370
- package/server/app/commandsManager.ts +393 -0
- package/server/app/container/config.ts +11 -49
- package/server/app/container/console/index.ts +2 -3
- package/server/app/container/index.ts +5 -2
- package/server/app/container/trace/index.ts +364 -0
- package/server/app/devCommands.ts +192 -0
- package/server/app/devDiagnostics.ts +53 -0
- package/server/app/index.ts +29 -6
- package/server/index.ts +0 -1
- package/server/services/auth/index.ts +525 -61
- package/server/services/auth/router/index.ts +106 -7
- package/server/services/cron/CronTask.ts +73 -5
- package/server/services/cron/index.ts +34 -11
- package/server/services/fetch/index.ts +3 -10
- package/server/services/prisma/index.ts +66 -4
- package/server/services/router/http/index.ts +173 -6
- package/server/services/router/index.ts +200 -12
- package/server/services/router/request/api.ts +30 -1
- package/server/services/router/response/index.ts +83 -10
- package/server/services/router/response/page/document.tsx +16 -0
- package/server/services/router/response/page/index.tsx +27 -1
- package/skills/clean-project-code/SKILL.md +7 -2
- package/test-results/.last-run.json +4 -0
- package/types/aliases.d.ts +6 -0
- package/types/global/utils.d.ts +7 -14
- package/Rte.zip +0 -0
- package/agents/project/agents.md.zip +0 -0
- package/doc/TODO.md +0 -71
- package/doc/front/router.md +0 -27
- package/doc/workspace/workspace.png +0 -0
- package/doc/workspace/workspace2.png +0 -0
- package/doc/workspace/workspace_26.01.22.png +0 -0
- package/server/services/router/http/session.ts.old +0 -40
|
@@ -28,6 +28,11 @@ export type TProps = { service?: ClientRouter; loaderComponent?: React.Component
|
|
|
28
28
|
----------------------------------*/
|
|
29
29
|
|
|
30
30
|
const LogPrefix = `[router][component]`;
|
|
31
|
+
const withProfiler = <T,>(callback: (runtime: (typeof import('@client/dev/profiler/runtime'))['profilerRuntime']) => T) => {
|
|
32
|
+
if (!__DEV__) return undefined as T | undefined;
|
|
33
|
+
const profilerModule = require('@client/dev/profiler/runtime') as typeof import('@client/dev/profiler/runtime');
|
|
34
|
+
return callback(profilerModule.profilerRuntime);
|
|
35
|
+
};
|
|
31
36
|
|
|
32
37
|
const PageLoading = ({
|
|
33
38
|
clientRouter,
|
|
@@ -91,6 +96,12 @@ export default ({ service: clientRouter, loaderComponent }: TProps) => {
|
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
// Set loading state
|
|
99
|
+
const sessionId = withProfiler((runtime) =>
|
|
100
|
+
runtime.startNavigationSession({
|
|
101
|
+
path: request.path,
|
|
102
|
+
url: request.url,
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
94
105
|
clientRouter.runHook('page.change', request);
|
|
95
106
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
96
107
|
clientRouter.setLoading(true);
|
|
@@ -99,21 +110,25 @@ export default ({ service: clientRouter, loaderComponent }: TProps) => {
|
|
|
99
110
|
// Unable to load (no connection, server error, ....)
|
|
100
111
|
if (newpage === null) return;
|
|
101
112
|
|
|
102
|
-
return await changePage(newpage, data, request);
|
|
113
|
+
return await changePage(newpage, data, request, sessionId);
|
|
103
114
|
};
|
|
104
115
|
|
|
105
|
-
async function changePage(newpage: Page, data?: {}, request?: ClientRequest) {
|
|
116
|
+
async function changePage(newpage: Page, data?: {}, request?: ClientRequest, sessionId?: string) {
|
|
106
117
|
// Fetch API data to hydrate the page
|
|
107
118
|
try {
|
|
108
119
|
await newpage.preRender();
|
|
109
120
|
} catch (error) {
|
|
110
121
|
console.error(LogPrefix, 'Unable to fetch data:', error);
|
|
122
|
+
withProfiler((runtime) =>
|
|
123
|
+
runtime.failNavigation(error instanceof Error ? error.message : String(error), sessionId),
|
|
124
|
+
);
|
|
111
125
|
clientRouter?.setLoading(false);
|
|
112
126
|
return;
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
// Add additional data
|
|
116
130
|
if (data) newpage.data = { ...newpage.data, ...data };
|
|
131
|
+
withProfiler((runtime) => runtime.startRenderStep(sessionId));
|
|
117
132
|
|
|
118
133
|
// Add page container
|
|
119
134
|
setCurrentPage((page) => {
|
|
@@ -172,6 +187,19 @@ export default ({ service: clientRouter, loaderComponent }: TProps) => {
|
|
|
172
187
|
currentPage?.updateClient();
|
|
173
188
|
// Scroll to the selected content via url hash
|
|
174
189
|
restoreScroll(currentPage);
|
|
190
|
+
const routeLabel =
|
|
191
|
+
currentPage && 'path' in currentPage.route && currentPage.route.path
|
|
192
|
+
? currentPage.route.path
|
|
193
|
+
: currentPage && 'code' in currentPage.route
|
|
194
|
+
? String(currentPage.route.code)
|
|
195
|
+
: undefined;
|
|
196
|
+
withProfiler((runtime) =>
|
|
197
|
+
runtime.finishNavigation({
|
|
198
|
+
chunkId: currentPage?.chunkId,
|
|
199
|
+
routeLabel,
|
|
200
|
+
title: currentPage?.title,
|
|
201
|
+
}),
|
|
202
|
+
);
|
|
175
203
|
|
|
176
204
|
// Hooks
|
|
177
205
|
clientRouter.runHook('page.changed', (currentPage?.context.request || context.request) as ClientRequest);
|
|
@@ -21,7 +21,6 @@ import BaseRouter, {
|
|
|
21
21
|
TErrorRoute,
|
|
22
22
|
TRouteOptions,
|
|
23
23
|
TRouteModule,
|
|
24
|
-
TDomainsList,
|
|
25
24
|
matchRoute,
|
|
26
25
|
buildUrl,
|
|
27
26
|
} from '@common/router';
|
|
@@ -53,6 +52,11 @@ import appRoutes from '@generated/client/routes';
|
|
|
53
52
|
const debug = false;
|
|
54
53
|
const LogPrefix = '[router]';
|
|
55
54
|
const browserWindow = window as Window & { routes?: TSsrUnresolvedRoute[]; ssr?: TBasicSSrData };
|
|
55
|
+
const withProfiler = <T,>(callback: (runtime: (typeof import('@client/dev/profiler/runtime'))['profilerRuntime']) => T) => {
|
|
56
|
+
if (!__DEV__) return undefined as T | undefined;
|
|
57
|
+
const profilerModule = require('@client/dev/profiler/runtime') as typeof import('@client/dev/profiler/runtime');
|
|
58
|
+
return callback(profilerModule.profilerRuntime);
|
|
59
|
+
};
|
|
56
60
|
|
|
57
61
|
/*----------------------------------
|
|
58
62
|
- TYPES
|
|
@@ -136,7 +140,7 @@ export default class ClientRouter<
|
|
|
136
140
|
// Context data
|
|
137
141
|
public ssrRoutes = browserWindow.routes || [];
|
|
138
142
|
public ssrContext = browserWindow.ssr;
|
|
139
|
-
public
|
|
143
|
+
public currentDomain = browserWindow.ssr?.currentDomain || window.location.origin;
|
|
140
144
|
public context!: TRouterContext<this, this['app']>;
|
|
141
145
|
|
|
142
146
|
public setLoading!: React.Dispatch<React.SetStateAction<boolean>>;
|
|
@@ -153,7 +157,7 @@ export default class ClientRouter<
|
|
|
153
157
|
}
|
|
154
158
|
|
|
155
159
|
public url = (path: string, params: {} = {}, absolute: boolean = true) =>
|
|
156
|
-
buildUrl(path, params, this.
|
|
160
|
+
buildUrl(path, params, this.currentDomain, absolute);
|
|
157
161
|
|
|
158
162
|
public go(url: string | number, data: {} = {}, opt: { newTab?: boolean } = {}) {
|
|
159
163
|
// Error code
|
|
@@ -321,12 +325,19 @@ export default class ClientRouter<
|
|
|
321
325
|
|
|
322
326
|
// Create response
|
|
323
327
|
debug && console.log(LogPrefix, 'Resolved request', request.path, '| Route:', route);
|
|
328
|
+
withProfiler((runtime) =>
|
|
329
|
+
runtime.completeResolveStep({
|
|
330
|
+
chunkId: 'chunk' in route ? route.chunk : route.options.id,
|
|
331
|
+
routeLabel: request.path,
|
|
332
|
+
}),
|
|
333
|
+
);
|
|
324
334
|
const page = await this.createResponse(route, request);
|
|
325
335
|
|
|
326
336
|
return page;
|
|
327
337
|
}
|
|
328
338
|
|
|
329
339
|
const notFoundRoute = this.errors[404];
|
|
340
|
+
withProfiler((runtime) => runtime.completeResolveStep({ routeLabel: '404' }));
|
|
330
341
|
return await this.createResponse(notFoundRoute, request, { error: new Error('Page not found') });
|
|
331
342
|
}
|
|
332
343
|
|
|
@@ -338,16 +349,21 @@ export default class ClientRouter<
|
|
|
338
349
|
//throw new Error(`Failed to load route: ${route.chunk}`);
|
|
339
350
|
|
|
340
351
|
debug && console.log(`Fetching route ${route.chunk} ...`, route);
|
|
352
|
+
const stepId = withProfiler((runtime) => runtime.startChunkStep(route.chunk));
|
|
341
353
|
try {
|
|
342
354
|
const loaded = await route.load();
|
|
343
355
|
const fetched = loaded.__register(this.app);
|
|
344
356
|
|
|
345
357
|
debug && console.log(`Route fetched: ${route.chunk}`, fetched);
|
|
358
|
+
withProfiler((runtime) => runtime.finishStep(stepId));
|
|
346
359
|
|
|
347
360
|
if ('code' in route) return fetched as TClientPageErrorRoute<this>;
|
|
348
361
|
|
|
349
362
|
return { ...(fetched as TClientPageRoute<this>), regex: route.regex, keys: route.keys };
|
|
350
363
|
} catch (e) {
|
|
364
|
+
withProfiler((runtime) =>
|
|
365
|
+
runtime.finishStep(stepId, 'error', e instanceof Error ? e.message : String(e)),
|
|
366
|
+
);
|
|
351
367
|
console.error(`Failed to fetch the route ${route.chunk}`, e);
|
|
352
368
|
try {
|
|
353
369
|
this.app.handleUpdate();
|
|
@@ -368,6 +384,13 @@ export default class ClientRouter<
|
|
|
368
384
|
if (!route) throw new Error(`Unable to resolve route.`);
|
|
369
385
|
|
|
370
386
|
const request = new ClientRequest(location, this);
|
|
387
|
+
withProfiler((runtime) =>
|
|
388
|
+
runtime.ensureInitialSession({
|
|
389
|
+
path: request.path,
|
|
390
|
+
requestId: this.ssrContext?.request.id,
|
|
391
|
+
url: request.url,
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
371
394
|
|
|
372
395
|
// Restituate SSR response
|
|
373
396
|
let apiData: {} = {};
|
|
@@ -385,6 +408,7 @@ export default class ClientRouter<
|
|
|
385
408
|
|
|
386
409
|
ReactDOM.hydrate(<App context={response.context as AppPropsContext} />, document.body, () => {
|
|
387
410
|
console.log(`Render complete`);
|
|
411
|
+
withProfiler((runtime) => runtime.markInitialHydrated({ chunkId: response.chunkId, title: response.title }));
|
|
388
412
|
|
|
389
413
|
this.runHook('page.rendered', request);
|
|
390
414
|
});
|
|
@@ -24,6 +24,16 @@ import { toMultipart } from './multipart';
|
|
|
24
24
|
----------------------------------*/
|
|
25
25
|
|
|
26
26
|
const debug = false;
|
|
27
|
+
const getProfilerModule = () => {
|
|
28
|
+
if (!__DEV__) return undefined;
|
|
29
|
+
return require('@client/dev/profiler/runtime') as typeof import('@client/dev/profiler/runtime');
|
|
30
|
+
};
|
|
31
|
+
const withProfiler = <T>(callback: (runtime: (typeof import('@client/dev/profiler/runtime'))['profilerRuntime']) => T) => {
|
|
32
|
+
const profilerModule = getProfilerModule();
|
|
33
|
+
return profilerModule ? callback(profilerModule.profilerRuntime) : undefined;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
type TExecuteResult<TData> = { data: TData; durationMs: number; response: Response };
|
|
27
37
|
|
|
28
38
|
export type Config = {};
|
|
29
39
|
|
|
@@ -126,8 +136,54 @@ export default class ApiClient implements ApiClientService {
|
|
|
126
136
|
): Promise<TData> {
|
|
127
137
|
/*if (options?.captcha !== undefined)
|
|
128
138
|
await this.gui.captcha.check(options?.captcha);*/
|
|
139
|
+
const pendingTrace = withProfiler((runtime) =>
|
|
140
|
+
runtime.startTrace('async', {
|
|
141
|
+
label: `${method} ${path}`,
|
|
142
|
+
method,
|
|
143
|
+
path,
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const result = await this.executeDetailed<TData>('client-async', method, path, data, options);
|
|
149
|
+
const profilerModule = getProfilerModule();
|
|
150
|
+
const traceRequestId = profilerModule?.readProfilerTraceRequestId(result.response);
|
|
151
|
+
|
|
152
|
+
if (pendingTrace && traceRequestId) {
|
|
153
|
+
await profilerModule?.profilerRuntime.attachTraceByRequestId(
|
|
154
|
+
pendingTrace.sessionId,
|
|
155
|
+
pendingTrace.traceId,
|
|
156
|
+
traceRequestId,
|
|
157
|
+
);
|
|
158
|
+
} else if (pendingTrace) {
|
|
159
|
+
withProfiler((runtime) =>
|
|
160
|
+
runtime.completeTrace(pendingTrace.traceId, {
|
|
161
|
+
durationMs: result.durationMs,
|
|
162
|
+
status: 'completed',
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
}
|
|
129
166
|
|
|
130
|
-
|
|
167
|
+
return result.data;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
const profilerModule = getProfilerModule();
|
|
170
|
+
const errorResponse = (error as Error & { response?: Response }).response;
|
|
171
|
+
const traceRequestId = errorResponse ? profilerModule?.readProfilerTraceRequestId(errorResponse) : undefined;
|
|
172
|
+
if (pendingTrace && traceRequestId) {
|
|
173
|
+
await profilerModule?.profilerRuntime.attachTraceByRequestId(
|
|
174
|
+
pendingTrace.sessionId,
|
|
175
|
+
pendingTrace.traceId,
|
|
176
|
+
traceRequestId,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
withProfiler((runtime) =>
|
|
180
|
+
runtime.completeTrace(pendingTrace?.traceId, {
|
|
181
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
182
|
+
status: 'error',
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
throw error;
|
|
186
|
+
}
|
|
131
187
|
}
|
|
132
188
|
|
|
133
189
|
public async fetchSync(fetchers: TFetcherList, alreadyLoadedData: {}): Promise<TObjetDonnees> {
|
|
@@ -145,23 +201,68 @@ export default class ApiClient implements ApiClientService {
|
|
|
145
201
|
const fetchedData =
|
|
146
202
|
fetchersCount === 0
|
|
147
203
|
? {}
|
|
148
|
-
: await
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
204
|
+
: await (async () => {
|
|
205
|
+
const pendingTrace = withProfiler((runtime) =>
|
|
206
|
+
runtime.startTrace('navigation-data', {
|
|
207
|
+
fetcherIds: Object.keys(fetchersToRun),
|
|
208
|
+
label: 'Navigation data',
|
|
209
|
+
method: 'POST',
|
|
210
|
+
path: '/api',
|
|
211
|
+
}),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
try {
|
|
215
|
+
const result = await this.executeDetailed<TObjetDonnees>(
|
|
216
|
+
'client-navigation',
|
|
217
|
+
'POST',
|
|
218
|
+
'/api',
|
|
219
|
+
({ fetchers: fetchersToRun } as unknown) as TPostData,
|
|
220
|
+
);
|
|
221
|
+
const profilerModule = getProfilerModule();
|
|
222
|
+
const traceRequestId = profilerModule?.readProfilerTraceRequestId(result.response);
|
|
223
|
+
|
|
224
|
+
if (pendingTrace && traceRequestId) {
|
|
225
|
+
await profilerModule?.profilerRuntime.attachTraceByRequestId(
|
|
226
|
+
pendingTrace.sessionId,
|
|
227
|
+
pendingTrace.traceId,
|
|
228
|
+
traceRequestId,
|
|
229
|
+
);
|
|
230
|
+
} else if (pendingTrace) {
|
|
231
|
+
withProfiler((runtime) =>
|
|
232
|
+
runtime.completeTrace(pendingTrace.traceId, {
|
|
233
|
+
durationMs: result.durationMs,
|
|
234
|
+
status: 'completed',
|
|
235
|
+
}),
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const responseData: TObjetDonnees = {};
|
|
240
|
+
for (const id in result.data) responseData[id] = result.data[id];
|
|
241
|
+
return responseData;
|
|
242
|
+
} catch (e) {
|
|
243
|
+
const profilerModule = getProfilerModule();
|
|
244
|
+
const errorResponse = (e as Error & { response?: Response }).response;
|
|
245
|
+
const traceRequestId = errorResponse ? profilerModule?.readProfilerTraceRequestId(errorResponse) : undefined;
|
|
246
|
+
if (pendingTrace && traceRequestId) {
|
|
247
|
+
await profilerModule?.profilerRuntime.attachTraceByRequestId(
|
|
248
|
+
pendingTrace.sessionId,
|
|
249
|
+
pendingTrace.traceId,
|
|
250
|
+
traceRequestId,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
withProfiler((runtime) =>
|
|
254
|
+
runtime.completeTrace(pendingTrace?.traceId, {
|
|
255
|
+
errorMessage: e instanceof Error ? e.message : String(e),
|
|
256
|
+
status: 'error',
|
|
257
|
+
}),
|
|
258
|
+
);
|
|
259
|
+
|
|
160
260
|
// API Error hook
|
|
161
|
-
this.app.handleError(e);
|
|
261
|
+
this.app.handleError(e as Error);
|
|
162
262
|
|
|
163
263
|
throw e;
|
|
164
|
-
}
|
|
264
|
+
}
|
|
265
|
+
})();
|
|
165
266
|
|
|
166
267
|
// Errors will be catched in the caller
|
|
167
268
|
|
|
@@ -214,21 +315,36 @@ export default class ApiClient implements ApiClientService {
|
|
|
214
315
|
};
|
|
215
316
|
|
|
216
317
|
public execute<TData = unknown>(...args: TFetcherArgs): Promise<TData> {
|
|
318
|
+
return this.executeDetailed<TData>('client-async', ...args).then((result) => result.data);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private async executeDetailed<TData = unknown>(
|
|
322
|
+
profilerOrigin: string,
|
|
323
|
+
...args: TFetcherArgs
|
|
324
|
+
): Promise<TExecuteResult<TData>> {
|
|
217
325
|
const { url, config } = this.configure(...args);
|
|
326
|
+
const startedAt = Date.now();
|
|
327
|
+
const headers = config.headers instanceof Headers ? config.headers : new Headers(config.headers as HeadersInit);
|
|
328
|
+
const profilerHeaders = withProfiler((runtime) => runtime.getRequestHeaders(profilerOrigin)) || {};
|
|
329
|
+
for (const [key, value] of Object.entries(profilerHeaders)) headers.set(key, value);
|
|
330
|
+
config.headers = headers;
|
|
218
331
|
|
|
219
332
|
console.log(`[api] Fetching`, url, config);
|
|
220
333
|
|
|
221
334
|
return fetch(url, config)
|
|
222
335
|
.then(async (response) => {
|
|
336
|
+
const requestDurationMs = Math.max(0, Date.now() - startedAt);
|
|
223
337
|
if (!response.ok) {
|
|
224
338
|
const errorData = await response.json();
|
|
225
339
|
console.warn(`[api] Failure:`, response.status, errorData);
|
|
226
|
-
const error = errorFromJson(errorData);
|
|
340
|
+
const error = errorFromJson(errorData) as Error & { durationMs?: number; response?: Response };
|
|
341
|
+
error.durationMs = requestDurationMs;
|
|
342
|
+
error.response = response;
|
|
227
343
|
throw error;
|
|
228
344
|
}
|
|
229
345
|
const json = (await response.json()) as TData;
|
|
230
346
|
debug && console.log(`[api] Success:`, json);
|
|
231
|
-
return json;
|
|
347
|
+
return { data: json, durationMs: requestDurationMs, response };
|
|
232
348
|
})
|
|
233
349
|
.catch((error) => {
|
|
234
350
|
if (error instanceof TypeError) {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Commands } from '@server/app/commands';
|
|
2
|
+
|
|
3
|
+
export default class ProteumDiagnosticsCommands extends Commands {
|
|
4
|
+
public async ping() {
|
|
5
|
+
return {
|
|
6
|
+
app: this.app.identity.identifier,
|
|
7
|
+
envProfile: this.app.env.profile,
|
|
8
|
+
services: Object.keys(this.app.getRootServices()),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { TTraceSummaryValue } from './requestTrace';
|
|
2
|
+
|
|
3
|
+
export type TDevCommandScope = 'app' | 'framework';
|
|
4
|
+
export type TDevCommandSourceLocation = { line: number; column: number };
|
|
5
|
+
export type TDevCommandExecutionStatus = 'completed' | 'error';
|
|
6
|
+
|
|
7
|
+
export type TDevCommandDefinition = {
|
|
8
|
+
path: string;
|
|
9
|
+
className: string;
|
|
10
|
+
methodName: string;
|
|
11
|
+
importPath: string;
|
|
12
|
+
filepath: string;
|
|
13
|
+
sourceLocation: TDevCommandSourceLocation;
|
|
14
|
+
scope: TDevCommandScope;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type TDevCommandSerializedResult = {
|
|
18
|
+
json?: unknown;
|
|
19
|
+
summary: TTraceSummaryValue;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type TDevCommandExecution = {
|
|
23
|
+
command: TDevCommandDefinition;
|
|
24
|
+
startedAt: string;
|
|
25
|
+
finishedAt: string;
|
|
26
|
+
durationMs: number;
|
|
27
|
+
status: TDevCommandExecutionStatus;
|
|
28
|
+
result?: TDevCommandSerializedResult;
|
|
29
|
+
errorMessage?: string;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type TDevCommandListResponse = {
|
|
33
|
+
commands: TDevCommandDefinition[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type TDevCommandRunResponse = {
|
|
37
|
+
execution: TDevCommandExecution;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type TDevCommandErrorResponse = {
|
|
41
|
+
error: string;
|
|
42
|
+
execution?: TDevCommandExecution;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const normalizeDevCommandPath = (value: string) =>
|
|
46
|
+
value
|
|
47
|
+
.trim()
|
|
48
|
+
.replace(/^\/+/, '')
|
|
49
|
+
.replace(/\/+$/, '')
|
|
50
|
+
.replace(/\/{2,}/g, '/');
|