proteum 2.2.0 → 2.2.2-1
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 +5 -3
- package/README.md +14 -3
- package/agents/project/AGENTS.md +14 -10
- package/agents/project/app-root/AGENTS.md +2 -2
- package/agents/project/diagnostics.md +2 -2
- package/agents/project/root/AGENTS.md +9 -7
- package/agents/project/tests/AGENTS.md +8 -1
- package/cli/commands/configure.ts +12 -3
- package/cli/commands/dev.ts +162 -12
- package/cli/compiler/common/index.ts +16 -0
- package/cli/presentation/commands.ts +1 -0
- package/cli/presentation/help.ts +4 -0
- package/cli/utils/agents.ts +36 -0
- package/common/dev/requestTrace.ts +66 -0
- package/common/env/proteumEnv.ts +10 -3
- package/docs/assets/unique-domains-chip.png +0 -0
- package/docs/request-tracing.md +17 -3
- package/package.json +1 -1
- package/server/app/container/trace/index.ts +255 -74
- package/server/services/prisma/index.ts +15 -12
- package/server/services/router/http/index.ts +1 -1
- package/server/services/router/index.ts +41 -9
- package/server/services/router/request/index.ts +21 -2
|
@@ -132,7 +132,13 @@ export type Config<
|
|
|
132
132
|
// Set it as a function, so when we instanciate the services, we can callthis.router to pass the router instance in roiuter services
|
|
133
133
|
type TRouterServicesList = { [serviceName: string]: AnyRouterService };
|
|
134
134
|
|
|
135
|
-
export type Hooks = {
|
|
135
|
+
export type Hooks = {
|
|
136
|
+
request: { args: [request: ServerRequest<TServerRouter>] };
|
|
137
|
+
'request.finished': { args: [request: ServerRequest<TServerRouter>] };
|
|
138
|
+
resolve: { args: [request: ServerRequest<TServerRouter>] };
|
|
139
|
+
resolved: { args: [route: TMatchedRoute, request: ServerRequest<TServerRouter>, response: ServerResponse<TServerRouter>] };
|
|
140
|
+
render: { args: [page: Page<TServerRouter>] };
|
|
141
|
+
};
|
|
136
142
|
|
|
137
143
|
export type TControllerDefinition = {
|
|
138
144
|
path?: string;
|
|
@@ -607,6 +613,32 @@ export default class ServerRouter<
|
|
|
607
613
|
/*----------------------------------
|
|
608
614
|
- RESOLUTION
|
|
609
615
|
----------------------------------*/
|
|
616
|
+
private async finalizeRequest(
|
|
617
|
+
request: ServerRequest<this>,
|
|
618
|
+
output: {
|
|
619
|
+
statusCode: number;
|
|
620
|
+
user?: string;
|
|
621
|
+
errorMessage?: string;
|
|
622
|
+
},
|
|
623
|
+
) {
|
|
624
|
+
this.app.container.Trace.finishRequest(request.id, output);
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
await this.runHook('request.finished', request);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
const typedError =
|
|
630
|
+
error instanceof Error ? error : new Error(typeof error === 'string' ? error : 'Unknown request.finished hook error');
|
|
631
|
+
|
|
632
|
+
try {
|
|
633
|
+
await this.app.runHook('error', typedError, request);
|
|
634
|
+
} catch (hookError) {
|
|
635
|
+
console.error('request.finished hook error', typedError, 'Error hook failure', hookError);
|
|
636
|
+
}
|
|
637
|
+
} finally {
|
|
638
|
+
this.app.container.Trace.releaseRequest(request.id);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
610
642
|
public async middleware(req: express.Request, res: express.Response) {
|
|
611
643
|
// Create request
|
|
612
644
|
let requestId = uuid();
|
|
@@ -629,7 +661,7 @@ export default class ServerRouter<
|
|
|
629
661
|
this,
|
|
630
662
|
);
|
|
631
663
|
|
|
632
|
-
this.app.container.Trace.startRequest({
|
|
664
|
+
request.profiling = this.app.container.Trace.startRequest({
|
|
633
665
|
id: request.id,
|
|
634
666
|
method: request.method,
|
|
635
667
|
path: request.path,
|
|
@@ -640,7 +672,7 @@ export default class ServerRouter<
|
|
|
640
672
|
profilerOrigin: request.headers[profilerOriginHeader] || undefined,
|
|
641
673
|
profilerParentRequestId: request.headers[profilerParentRequestIdHeader] || undefined,
|
|
642
674
|
});
|
|
643
|
-
if (this.app.container.Trace.
|
|
675
|
+
if (this.app.container.Trace.isDevTraceEnabled()) res.setHeader(profilerTraceRequestIdHeader, request.id);
|
|
644
676
|
if (cachedPage) {
|
|
645
677
|
this.app.container.Trace.record(
|
|
646
678
|
request.id,
|
|
@@ -659,7 +691,7 @@ export default class ServerRouter<
|
|
|
659
691
|
// Bulk API Requests
|
|
660
692
|
if (request.path === '/api' && typeof request.data.fetchers === 'object') {
|
|
661
693
|
await this.resolveApiBatch(request.data.fetchers, request);
|
|
662
|
-
this.
|
|
694
|
+
await this.finalizeRequest(request, {
|
|
663
695
|
statusCode: request.res.statusCode || 200,
|
|
664
696
|
user: request.user?.email,
|
|
665
697
|
});
|
|
@@ -696,7 +728,7 @@ export default class ServerRouter<
|
|
|
696
728
|
},
|
|
697
729
|
'summary',
|
|
698
730
|
);
|
|
699
|
-
this.
|
|
731
|
+
await this.finalizeRequest(request, {
|
|
700
732
|
statusCode: response.statusCode,
|
|
701
733
|
user: request.user?.email,
|
|
702
734
|
});
|
|
@@ -715,7 +747,7 @@ export default class ServerRouter<
|
|
|
715
747
|
'summary',
|
|
716
748
|
);
|
|
717
749
|
res.send(cachedPage.rendered);
|
|
718
|
-
this.
|
|
750
|
+
await this.finalizeRequest(request, {
|
|
719
751
|
statusCode: response.statusCode,
|
|
720
752
|
user: request.user?.email,
|
|
721
753
|
});
|
|
@@ -739,19 +771,19 @@ export default class ServerRouter<
|
|
|
739
771
|
'summary',
|
|
740
772
|
);
|
|
741
773
|
res.send(response.data);
|
|
742
|
-
this.
|
|
774
|
+
await this.finalizeRequest(request, {
|
|
743
775
|
statusCode: response.statusCode,
|
|
744
776
|
user: request.user?.email,
|
|
745
777
|
});
|
|
746
778
|
} else if (response.data !== 'true') {
|
|
747
|
-
this.
|
|
779
|
+
await this.finalizeRequest(request, {
|
|
748
780
|
statusCode: res.statusCode || response.statusCode,
|
|
749
781
|
user: request.user?.email,
|
|
750
782
|
errorMessage: "Can't return data from the controller since response has already been sent via express.",
|
|
751
783
|
});
|
|
752
784
|
throw new Error("Can't return data from the controller since response has already been sent via express.");
|
|
753
785
|
} else {
|
|
754
|
-
this.
|
|
786
|
+
await this.finalizeRequest(request, {
|
|
755
787
|
statusCode: res.statusCode || response.statusCode,
|
|
756
788
|
user: request.user?.email,
|
|
757
789
|
});
|
|
@@ -10,7 +10,7 @@ import Bowser from 'bowser';
|
|
|
10
10
|
|
|
11
11
|
// Core
|
|
12
12
|
import BaseRequest from '@common/router/request';
|
|
13
|
-
import type { TTraceCallOrigin } from '@common/dev/requestTrace';
|
|
13
|
+
import type { TRequestProfiling, TTraceCallOrigin } from '@common/dev/requestTrace';
|
|
14
14
|
|
|
15
15
|
// Specific
|
|
16
16
|
import type { HttpMethod, HttpHeaders } from '..';
|
|
@@ -42,6 +42,24 @@ type TRequestTraceCallContext = {
|
|
|
42
42
|
label: string;
|
|
43
43
|
origin: TTraceCallOrigin;
|
|
44
44
|
};
|
|
45
|
+
type THeaderValue = string | string[] | undefined;
|
|
46
|
+
|
|
47
|
+
const readHeader = (headers: Record<string, THeaderValue>, name: string): string | null => {
|
|
48
|
+
const exact = headers[name];
|
|
49
|
+
const alternate = exact === undefined ? headers[name.toLowerCase()] : exact;
|
|
50
|
+
const value = alternate === undefined ? headers[name.toUpperCase()] : alternate;
|
|
51
|
+
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
const firstValue = value.find((entry) => typeof entry === 'string' && entry.trim());
|
|
54
|
+
return typeof firstValue === 'string' ? firstValue.trim() : null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const resolveRequestIp = (req: express.Request): string | undefined => {
|
|
61
|
+
return readHeader(req.headers as Record<string, THeaderValue>, 'cf-connecting-ip') || req.ip;
|
|
62
|
+
};
|
|
45
63
|
|
|
46
64
|
/*----------------------------------
|
|
47
65
|
- CONTEXTE
|
|
@@ -74,6 +92,7 @@ export default class ServerRequest<TRouter extends TAnyRouter = TAnyRouter> exte
|
|
|
74
92
|
// Services
|
|
75
93
|
public api: ApiClient;
|
|
76
94
|
public traceCall?: TRequestTraceCallContext;
|
|
95
|
+
public profiling!: TRequestProfiling;
|
|
77
96
|
|
|
78
97
|
/*----------------------------------
|
|
79
98
|
- INITIALISATION
|
|
@@ -107,7 +126,7 @@ export default class ServerRequest<TRouter extends TAnyRouter = TAnyRouter> exte
|
|
|
107
126
|
this.domain = res.req.hostname;
|
|
108
127
|
this.cookies = res.req.cookies;
|
|
109
128
|
|
|
110
|
-
this.ip = res.req
|
|
129
|
+
this.ip = resolveRequestIp(res.req);
|
|
111
130
|
|
|
112
131
|
this.data = data || {};
|
|
113
132
|
}
|