@solidstarters/solid-core 1.2.165 → 1.2.168
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/dist/config/iam.config.d.ts +2 -0
- package/dist/config/iam.config.d.ts.map +1 -1
- package/dist/config/iam.config.js +1 -0
- package/dist/config/iam.config.js.map +1 -1
- package/dist/decorators/error-codes-provider.decorator.d.ts +4 -0
- package/dist/decorators/error-codes-provider.decorator.d.ts.map +1 -0
- package/dist/decorators/error-codes-provider.decorator.js +12 -0
- package/dist/decorators/error-codes-provider.decorator.js.map +1 -0
- package/dist/dtos/post-chatter-message.dto.js.map +1 -1
- package/dist/entities/chatter-message.entity.js.map +1 -1
- package/dist/entities/security-rule.entity.js +0 -1
- package/dist/entities/security-rule.entity.js.map +1 -1
- package/dist/filters/http-exception.filter.d.ts +3 -2
- package/dist/filters/http-exception.filter.d.ts.map +1 -1
- package/dist/filters/http-exception.filter.js +23 -16
- package/dist/filters/http-exception.filter.js.map +1 -1
- package/dist/helpers/error-mapper.service.d.ts +12 -2
- package/dist/helpers/error-mapper.service.d.ts.map +1 -1
- package/dist/helpers/error-mapper.service.js +85 -72
- package/dist/helpers/error-mapper.service.js.map +1 -1
- package/dist/helpers/security.helper.d.ts +4 -2
- package/dist/helpers/security.helper.d.ts.map +1 -1
- package/dist/helpers/security.helper.js +38 -23
- package/dist/helpers/security.helper.js.map +1 -1
- package/dist/helpers/solid-core-error-codes-provider.service.d.ts +7 -0
- package/dist/helpers/solid-core-error-codes-provider.service.d.ts.map +1 -0
- package/dist/helpers/solid-core-error-codes-provider.service.js +67 -0
- package/dist/helpers/solid-core-error-codes-provider.service.js.map +1 -0
- package/dist/helpers/solid-registry.d.ts +5 -1
- package/dist/helpers/solid-registry.d.ts.map +1 -1
- package/dist/helpers/solid-registry.js +16 -0
- package/dist/helpers/solid-registry.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +16 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/repository/security-rule.repository.js +2 -2
- package/dist/repository/security-rule.repository.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +2 -2
- package/dist/services/authentication.service.js +5 -4
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/chatter-message.service.d.ts.map +1 -1
- package/dist/services/chatter-message.service.js.map +1 -1
- package/dist/services/model-metadata.service.js +1 -1
- package/dist/services/model-metadata.service.js.map +1 -1
- package/dist/services/setting.service.d.ts.map +1 -1
- package/dist/services/setting.service.js +2 -1
- package/dist/services/setting.service.js.map +1 -1
- package/dist/services/solid-introspect.service.d.ts +1 -0
- package/dist/services/solid-introspect.service.d.ts.map +1 -1
- package/dist/services/solid-introspect.service.js +14 -0
- package/dist/services/solid-introspect.service.js.map +1 -1
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +2 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config/iam.config.ts +1 -0
- package/src/decorators/error-codes-provider.decorator.ts +9 -0
- package/src/dtos/post-chatter-message.dto.ts +1 -1
- package/src/entities/chatter-message.entity.ts +3 -3
- package/src/entities/security-rule.entity.ts +1 -1
- package/src/filters/http-exception.filter.ts +48 -23
- package/src/helpers/error-mapper.service.ts +117 -176
- package/src/helpers/security.helper.ts +95 -30
- package/src/helpers/solid-core-error-codes-provider.service.ts +63 -0
- package/src/helpers/solid-registry.ts +20 -1
- package/src/index.ts +1 -0
- package/src/interfaces.ts +36 -0
- package/src/repository/security-rule.repository.ts +2 -2
- package/src/seeders/seed-data/solid-core-metadata.json +2 -2
- package/src/services/authentication.service.ts +6 -6
- package/src/services/chatter-message.service.ts +373 -374
- package/src/services/model-metadata.service.ts +1 -1
- package/src/services/setting.service.ts +2 -1
- package/src/services/solid-introspect.service.ts +22 -0
- package/src/solid-core.module.ts +2 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.168",
|
|
4
4
|
"description": "This module is a NestJS module containing all the required core providers required by a Solid application",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
package/src/config/iam.config.ts
CHANGED
|
@@ -22,6 +22,7 @@ export const iamConfig = registerAs('iam', () => {
|
|
|
22
22
|
callbackURL: process.env.IAM_GOOGLE_OAUTH_CALLBACK_URL,
|
|
23
23
|
redirectURL: process.env.IAM_GOOGLE_OAUTH_REDIRECT_URL,
|
|
24
24
|
},
|
|
25
|
+
iamAutoGeneratedPassword:process.env.IAM_AUTOGENERATED_PASSWORD || true
|
|
25
26
|
};
|
|
26
27
|
})
|
|
27
28
|
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { CommonEntity } from 'src/entities/common.entity'
|
|
2
|
-
import {Entity, Column, Index, JoinColumn, ManyToOne} from 'typeorm';
|
|
2
|
+
import { Entity, Column, Index, JoinColumn, ManyToOne } from 'typeorm';
|
|
3
3
|
import { User } from 'src/entities/user.entity'
|
|
4
4
|
|
|
5
5
|
@Entity("ss_chatter_message")
|
|
6
6
|
export class ChatterMessage extends CommonEntity {
|
|
7
7
|
@Index()
|
|
8
8
|
@Column({ type: "varchar" })
|
|
9
|
-
messageType: string;
|
|
9
|
+
messageType: string; // audit | custom
|
|
10
10
|
@Column({ type: "varchar" })
|
|
11
|
-
messageSubType: string;
|
|
11
|
+
messageSubType: string; // update | insert | delete | post_message
|
|
12
12
|
@Column({ type: "text" })
|
|
13
13
|
messageBody: string;
|
|
14
14
|
@Index()
|
|
@@ -1,39 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
1
|
+
// src/common/filters/http-exception.filter.ts
|
|
2
|
+
import {
|
|
3
|
+
ExceptionFilter,
|
|
4
|
+
Catch,
|
|
5
|
+
ArgumentsHost,
|
|
6
|
+
Logger,
|
|
7
|
+
Injectable,
|
|
8
|
+
HttpException,
|
|
9
|
+
} from '@nestjs/common';
|
|
10
|
+
import { Response, Request } from 'express';
|
|
3
11
|
import { HttpStatusCodeMessages } from '../interceptors/logging.interceptor';
|
|
12
|
+
import { ErrorMapperService } from 'src/helpers/error-mapper.service';
|
|
13
|
+
import { ErrorCode } from 'src/interfaces';
|
|
14
|
+
|
|
4
15
|
|
|
5
16
|
@Catch()
|
|
6
17
|
@Injectable()
|
|
7
18
|
export class HttpExceptionFilter implements ExceptionFilter {
|
|
8
19
|
private readonly logger = new Logger(HttpExceptionFilter.name);
|
|
9
20
|
|
|
10
|
-
constructor() {
|
|
21
|
+
constructor(private readonly errorMapper: ErrorMapperService) {
|
|
11
22
|
this.logger.debug('HttpExceptionFilter initialized');
|
|
12
|
-
}
|
|
23
|
+
}
|
|
13
24
|
|
|
14
25
|
catch(exception: any, host: ArgumentsHost) {
|
|
15
26
|
const ctx = host.switchToHttp();
|
|
16
27
|
const response = ctx.getResponse<Response>();
|
|
17
|
-
const request = ctx.getRequest();
|
|
18
|
-
const status = exception.status || 500;
|
|
19
|
-
const message = exception.message || 'Internal server error';
|
|
20
|
-
const { method, url } = request;
|
|
21
|
-
|
|
22
|
-
// Log the error here
|
|
23
|
-
this.logger.error(`[${status || 500} ${HttpStatusCodeMessages[status] || 'Internal Server Error'}] ${method} ${url} - ${message}`);
|
|
24
|
-
this.logger.error(exception.stack || 'No stack trace available');
|
|
25
|
-
|
|
26
|
-
// Send the response to the client
|
|
27
|
-
const errorJson = this.getErrorJson(status, message, exception.response);
|
|
28
|
-
response.status(status).json(errorJson);
|
|
29
|
-
}
|
|
28
|
+
const request = ctx.getRequest<Request>();
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
const isHttp = exception instanceof HttpException;
|
|
31
|
+
const explicitStatus = isHttp ? exception.getStatus() : undefined;
|
|
32
|
+
|
|
33
|
+
// Canonical code + static message
|
|
34
|
+
const code: ErrorCode = this.errorMapper.mapException(exception);
|
|
35
|
+
const defaultStatus = this.errorMapper.getHttpStatus(code);
|
|
36
|
+
const message = this.errorMapper.getMessage(code);
|
|
37
|
+
|
|
38
|
+
const status = explicitStatus ?? defaultStatus ?? 500;
|
|
39
|
+
|
|
40
|
+
// Logging
|
|
41
|
+
this.logger.error(
|
|
42
|
+
`[${status} ${HttpStatusCodeMessages[status] || 'Internal Server Error'}] ${request?.method} ${request?.url} - ${exception?.message || message} [code=${code}]`,
|
|
43
|
+
);
|
|
44
|
+
if (exception?.stack) {
|
|
45
|
+
this.logger.error(exception.stack);
|
|
37
46
|
}
|
|
47
|
+
|
|
48
|
+
// Preserve any extra data the exception carried (optional)
|
|
49
|
+
const extra =
|
|
50
|
+
(isHttp && (exception.getResponse?.() as any)) ??
|
|
51
|
+
exception?.response ??
|
|
52
|
+
{};
|
|
53
|
+
|
|
54
|
+
// Keep your legacy shape; add canonical code
|
|
55
|
+
response.status(status).json({
|
|
56
|
+
statusCode: status,
|
|
57
|
+
statusCodeMessage: HttpStatusCodeMessages[status] || 'Internal Server Error',
|
|
58
|
+
// message: [message],
|
|
59
|
+
errorCode: code,
|
|
60
|
+
error: message,
|
|
61
|
+
data: extra,
|
|
62
|
+
});
|
|
38
63
|
}
|
|
39
64
|
}
|
|
@@ -1,214 +1,155 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
+
import { SolidRegistry } from 'src/helpers/solid-registry';
|
|
3
|
+
import { ErrorCode, ErrorMeta, ErrorRule, IErrorCodeProvider } from 'src/interfaces';
|
|
4
|
+
|
|
5
|
+
// export const ERROR_CODES = [
|
|
6
|
+
// 'db-duplicate-key',
|
|
7
|
+
// 'db-foreign-key-error',
|
|
8
|
+
// 'solidx-mcp-server-unavailable',
|
|
9
|
+
// 'unknown-error',
|
|
10
|
+
// ] as const;
|
|
11
|
+
|
|
12
|
+
// export type ErrorCode = typeof ERROR_CODES[number];
|
|
13
|
+
|
|
14
|
+
// type ErrorMeta = {
|
|
15
|
+
// message: string;
|
|
16
|
+
// httpStatus?: number;
|
|
17
|
+
// };
|
|
18
|
+
|
|
19
|
+
// const ERROR_MESSAGES: Record<ErrorCode, ErrorMeta> = {
|
|
20
|
+
// 'db-duplicate-key': {
|
|
21
|
+
// message: 'Duplicate key violation. A record with these values already exists.',
|
|
22
|
+
// httpStatus: 409,
|
|
23
|
+
// },
|
|
24
|
+
// 'db-foreign-key-error': {
|
|
25
|
+
// message: 'Foreign key constraint prevents this operation due to related records.',
|
|
26
|
+
// httpStatus: 409,
|
|
27
|
+
// },
|
|
28
|
+
// 'solidx-mcp-server-unavailable': {
|
|
29
|
+
// message: 'SolidX MCP server is unreachable. Please verify the MCP endpoint.',
|
|
30
|
+
// httpStatus: 503,
|
|
31
|
+
// },
|
|
32
|
+
// 'unknown-error': {
|
|
33
|
+
// message: 'An unexpected error occurred.',
|
|
34
|
+
// httpStatus: 500,
|
|
35
|
+
// },
|
|
36
|
+
// };
|
|
19
37
|
|
|
20
38
|
@Injectable()
|
|
21
39
|
export class ErrorMapperService {
|
|
22
|
-
|
|
23
|
-
* Given an error/exception, return a mapped error code string.
|
|
24
|
-
* Default: "unknown-error"
|
|
25
|
-
*/
|
|
26
|
-
mapException(exc: unknown): ErrorCode {
|
|
27
|
-
const combined = this.combineErrorText(exc);
|
|
40
|
+
private readonly logger = new Logger(ErrorMapperService.name);
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
// {
|
|
31
|
-
// "success": false,
|
|
32
|
-
// "errors": [
|
|
33
|
-
// "unhandled errors in a TaskGroup (1 sub-exception)"
|
|
34
|
-
// ],
|
|
35
|
-
// "error_trace": [
|
|
36
|
-
// "Traceback (most recent call last):",
|
|
37
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/mcp/client/sse.py\", line 47, in sse_client\n async with aconnect_sse(\n ^^^^^^^^^^^^^",
|
|
38
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 210, in __aenter__\n return await anext(self.gen)\n ^^^^^^^^^^^^^^^^^^^^^",
|
|
39
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx_sse/_api.py\", line 69, in aconnect_sse\n async with client.stream(method, url, headers=headers, **kwargs) as response:\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
40
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 210, in __aenter__\n return await anext(self.gen)\n ^^^^^^^^^^^^^^^^^^^^^",
|
|
41
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1583, in stream\n response = await self.send(\n ^^^^^^^^^^^^^^^^",
|
|
42
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1629, in send\n response = await self._send_handling_auth(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
43
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1657, in _send_handling_auth\n response = await self._send_handling_redirects(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
44
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1694, in _send_handling_redirects\n response = await self._send_single_request(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
45
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1730, in _send_single_request\n response = await transport.handle_async_request(request)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
46
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_transports/default.py\", line 393, in handle_async_request\n with map_httpcore_exceptions():\n ^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
47
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 158, in __exit__\n self.gen.throw(value)",
|
|
48
|
-
// "File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_transports/default.py\", line 118, in map_httpcore_exceptions\n raise mapped_exc(message) from exc",
|
|
49
|
-
// "httpx.ConnectError: All connection attempts failed",
|
|
50
|
-
// "During handling of the above exception, another exception occurred:",
|
|
51
|
-
// "+ Exception Group Traceback (most recent call last):",
|
|
52
|
-
// "| File \"/Users/harishpatel/mcp/clients/solidx_mcp_client/client_sse_nochat.py\", line 239, in main\n | await client.connect_to_sse_server()",
|
|
53
|
-
// "| File \"/Users/harishpatel/mcp/clients/solidx_mcp_client/client_sse_nochat.py\", line 49, in connect_to_sse_server\n | streams = await self._streams_context.__aenter__()\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
54
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 210, in __aenter__\n | return await anext(self.gen)\n | ^^^^^^^^^^^^^^^^^^^^^",
|
|
55
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/mcp/client/sse.py\", line 43, in sse_client\n | async with anyio.create_task_group() as tg:\n | ^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
56
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/anyio/_backends/_asyncio.py\", line 767, in __aexit__\n | raise BaseExceptionGroup(",
|
|
57
|
-
// "| ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)",
|
|
58
|
-
// "+-+---------------- 1 ----------------",
|
|
59
|
-
// "| Traceback (most recent call last):",
|
|
60
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_transports/default.py\", line 101, in map_httpcore_exceptions\n | yield",
|
|
61
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_transports/default.py\", line 394, in handle_async_request\n | resp = await self._pool.handle_async_request(req)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
62
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_async/connection_pool.py\", line 256, in handle_async_request\n | raise exc from None",
|
|
63
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_async/connection_pool.py\", line 236, in handle_async_request\n | response = await connection.handle_async_request(\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
64
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_async/connection.py\", line 101, in handle_async_request\n | raise exc",
|
|
65
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_async/connection.py\", line 78, in handle_async_request\n | stream = await self._connect(request)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
66
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_async/connection.py\", line 124, in _connect\n | stream = await self._network_backend.connect_tcp(**kwargs)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
67
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_backends/auto.py\", line 31, in connect_tcp\n | return await self._backend.connect_tcp(\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
68
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_backends/anyio.py\", line 113, in connect_tcp\n | with map_exceptions(exc_map):\n | ^^^^^^^^^^^^^^^^^^^^^^^",
|
|
69
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 158, in __exit__\n | self.gen.throw(value)",
|
|
70
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpcore/_exceptions.py\", line 14, in map_exceptions\n | raise to_exc(exc) from exc",
|
|
71
|
-
// "| httpcore.ConnectError: All connection attempts failed",
|
|
72
|
-
// "| \n | The above exception was the direct cause of the following exception:\n |",
|
|
73
|
-
// "| Traceback (most recent call last):",
|
|
74
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/mcp/client/sse.py\", line 47, in sse_client\n | async with aconnect_sse(\n | ^^^^^^^^^^^^^",
|
|
75
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 210, in __aenter__\n | return await anext(self.gen)\n | ^^^^^^^^^^^^^^^^^^^^^",
|
|
76
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx_sse/_api.py\", line 69, in aconnect_sse\n | async with client.stream(method, url, headers=headers, **kwargs) as response:\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
77
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 210, in __aenter__\n | return await anext(self.gen)\n | ^^^^^^^^^^^^^^^^^^^^^",
|
|
78
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1583, in stream\n | response = await self.send(\n | ^^^^^^^^^^^^^^^^",
|
|
79
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1629, in send\n | response = await self._send_handling_auth(\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
80
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1657, in _send_handling_auth\n | response = await self._send_handling_redirects(\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
81
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1694, in _send_handling_redirects\n | response = await self._send_single_request(request)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
82
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_client.py\", line 1730, in _send_single_request\n | response = await transport.handle_async_request(request)\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
83
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_transports/default.py\", line 393, in handle_async_request\n | with map_httpcore_exceptions():\n | ^^^^^^^^^^^^^^^^^^^^^^^^^",
|
|
84
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/lib/python3.12/contextlib.py\", line 158, in __exit__\n | self.gen.throw(value)",
|
|
85
|
-
// "| File \"/Users/harishpatel/.pyenv/versions/3.12.7/envs/solid_mcp_client/lib/python3.12/site-packages/httpx/_transports/default.py\", line 118, in map_httpcore_exceptions\n | raise mapped_exc(message) from exc",
|
|
86
|
-
// "| httpx.ConnectError: All connection attempts failed",
|
|
87
|
-
// "+------------------------------------"
|
|
88
|
-
// ],
|
|
89
|
-
// "request": "\"Can you do 1 + 1\""
|
|
90
|
-
// }
|
|
91
|
-
if (combined.includes("all connection attempts failed") && combined.includes("unhandled errors in a taskgroup (1 sub-exception)")) {
|
|
92
|
-
return 'solidx-mcp-server-unavailable';
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// --- Bedrock errors ---
|
|
96
|
-
// Throttling: "ThrottlingException" or "Too many tokens"
|
|
97
|
-
if (
|
|
98
|
-
combined.includes('throttlingexception') ||
|
|
99
|
-
combined.includes('too many tokens')
|
|
100
|
-
) {
|
|
101
|
-
return 'bedrock-throttling-error';
|
|
102
|
-
}
|
|
42
|
+
constructor(private readonly solidRegistry: SolidRegistry) { }
|
|
103
43
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
combined.includes('validationexception') &&
|
|
110
|
-
combined.includes('input is too long')
|
|
111
|
-
) {
|
|
112
|
-
return 'bedrock-input-too-long';
|
|
113
|
-
}
|
|
44
|
+
/** Map an exception object (or string) to a canonical ErrorCode */
|
|
45
|
+
mapException(exc: unknown): ErrorCode {
|
|
46
|
+
const combined = this.combineErrorText(exc);
|
|
47
|
+
return this.matchCode(combined);
|
|
48
|
+
}
|
|
114
49
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
50
|
+
/** Map plain message/trace to ErrorCode */
|
|
51
|
+
mapMessage(message: string, trace?: string): ErrorCode {
|
|
52
|
+
const combined = `${message ?? ''}\n${trace ?? ''}`.toLowerCase();
|
|
53
|
+
return this.matchCode(combined);
|
|
54
|
+
}
|
|
118
55
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
56
|
+
/** Get static message for a given code */
|
|
57
|
+
getMessage(code: ErrorCode): string {
|
|
58
|
+
const meta = this.lookupMeta(code);
|
|
59
|
+
return (meta ?? { message: 'An unexpected error occurred.' }).message;
|
|
60
|
+
}
|
|
122
61
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return 'db-duplicate-key';
|
|
129
|
-
}
|
|
62
|
+
/** Get default HTTP status for a code (falls back to 500) */
|
|
63
|
+
getHttpStatus(code: ErrorCode): number {
|
|
64
|
+
const meta = this.lookupMeta(code);
|
|
65
|
+
return meta?.httpStatus ?? 500;
|
|
66
|
+
}
|
|
130
67
|
|
|
131
|
-
|
|
132
|
-
|
|
68
|
+
// ---- internal helpers ----
|
|
69
|
+
private matchCode(combined: string): ErrorCode {
|
|
70
|
+
const rules = this.getAllRulesSorted();
|
|
71
|
+
for (const rule of rules) {
|
|
72
|
+
try {
|
|
73
|
+
if (rule.match(combined)) return rule.code;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Defensive: bad provider shouldn't crash mapping
|
|
76
|
+
this.logger.warn(`Error rule threw in match(): code=${rule.code} provider? — ${e}`);
|
|
77
|
+
}
|
|
133
78
|
}
|
|
79
|
+
return 'unknown-error';
|
|
80
|
+
}
|
|
134
81
|
|
|
135
|
-
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
) {
|
|
144
|
-
|
|
82
|
+
private lookupMeta(code: ErrorCode): ErrorMeta | undefined {
|
|
83
|
+
// Prefer the first rule with that code
|
|
84
|
+
const rules = this.getAllRulesSorted();
|
|
85
|
+
const rule = rules.find((r) => r.code === code);
|
|
86
|
+
if (rule?.meta) return rule.meta;
|
|
87
|
+
|
|
88
|
+
// Optional: ask providers directly if they implement resolve()
|
|
89
|
+
const providers = this.getProviders();
|
|
90
|
+
for (const p of providers) {
|
|
91
|
+
if (p.resolve) {
|
|
92
|
+
const meta = p.resolve(code);
|
|
93
|
+
if (meta) return meta;
|
|
94
|
+
}
|
|
145
95
|
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
146
98
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
99
|
+
private getAllRulesSorted(): ReadonlyArray<ErrorRule> {
|
|
100
|
+
const providers = this.getProviders();
|
|
101
|
+
const all: ErrorRule[] = [];
|
|
102
|
+
for (const p of providers) {
|
|
103
|
+
try {
|
|
104
|
+
const rules = p.rules() ?? [];
|
|
105
|
+
// Optional: namespace collision check can be added here if desired
|
|
106
|
+
all.push(...rules);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
this.logger.warn(`ErrorCodeProvider.rules() failed for ${p.name?.()}: ${e}`);
|
|
109
|
+
}
|
|
151
110
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return 'unknown-error';
|
|
111
|
+
// Sort by priority desc; default 0
|
|
112
|
+
return all.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
|
|
155
113
|
}
|
|
156
114
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
115
|
+
private getProviders(): IErrorCodeProvider[] {
|
|
116
|
+
// convert InstanceWrapper → instance
|
|
117
|
+
return this.solidRegistry
|
|
118
|
+
.getErrorCodeProviders()
|
|
119
|
+
.map((w) => w.instance)
|
|
120
|
+
.filter(Boolean) as IErrorCodeProvider[];
|
|
163
121
|
}
|
|
164
122
|
|
|
165
|
-
// ---- helpers ----
|
|
166
|
-
|
|
167
123
|
private combineErrorText(exc: unknown): string {
|
|
168
|
-
|
|
169
|
-
if (typeof exc === 'string') {
|
|
170
|
-
return exc.toLowerCase();
|
|
171
|
-
}
|
|
124
|
+
if (typeof exc === 'string') return exc.toLowerCase();
|
|
172
125
|
|
|
173
|
-
// Standard Error
|
|
174
126
|
if (exc instanceof Error) {
|
|
175
127
|
const message = exc.message ?? '';
|
|
176
|
-
// Many libs set .stack to "Error: message\n<stack>"
|
|
177
|
-
// We still include it in case upstream mutated it.
|
|
178
128
|
const stack = exc.stack ?? '';
|
|
179
129
|
return `${message}\n${stack}`.toLowerCase();
|
|
180
130
|
}
|
|
181
131
|
|
|
182
|
-
// Some SDKs throw objects (e.g., { name, message, code, $metadata, ... })
|
|
183
132
|
if (exc && typeof exc === 'object') {
|
|
184
133
|
try {
|
|
185
|
-
const
|
|
186
|
-
const msg =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
const name =
|
|
191
|
-
String(maybeAny.name ?? '') ||
|
|
192
|
-
String(maybeAny['__type'] ?? '') ||
|
|
193
|
-
'';
|
|
194
|
-
const stack = String((maybeAny as any).stack ?? '');
|
|
195
|
-
// Also fold in a JSON snapshot as a last resort
|
|
196
|
-
const json = safeJsonStringify(maybeAny);
|
|
134
|
+
const obj = exc as Record<string, unknown>;
|
|
135
|
+
const msg = String(obj.message ?? (obj as any)['Message'] ?? '');
|
|
136
|
+
const name = String(obj.name ?? (obj as any)['__type'] ?? '');
|
|
137
|
+
const stack = String((obj as any).stack ?? '');
|
|
138
|
+
const json = this.safeJsonStringify(obj);
|
|
197
139
|
return `${name}\n${msg}\n${stack}\n${json}`.toLowerCase();
|
|
198
140
|
} catch {
|
|
199
|
-
//
|
|
141
|
+
// ignore
|
|
200
142
|
}
|
|
201
143
|
}
|
|
202
144
|
|
|
203
|
-
// Fallback
|
|
204
145
|
return String(exc ?? '').toLowerCase();
|
|
205
146
|
}
|
|
206
|
-
}
|
|
207
147
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
148
|
+
private safeJsonStringify(obj: unknown): string {
|
|
149
|
+
try {
|
|
150
|
+
return JSON.stringify(obj);
|
|
151
|
+
} catch {
|
|
152
|
+
return '';
|
|
153
|
+
}
|
|
213
154
|
}
|
|
214
155
|
}
|