@solidstarters/solid-core 1.2.163 → 1.2.166
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/controllers/test-queue.controller.d.ts +1 -1
- package/dist/controllers/test-queue.controller.d.ts.map +1 -1
- package/dist/controllers/test-queue.controller.js +6 -4
- package/dist/controllers/test-queue.controller.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/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/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/jobs/chatter-queue-options.d.ts +8 -0
- package/dist/jobs/chatter-queue-options.d.ts.map +1 -0
- package/dist/jobs/chatter-queue-options.js +10 -0
- package/dist/jobs/chatter-queue-options.js.map +1 -0
- package/dist/jobs/chatter-queue-publisher.service.d.ts +22 -0
- package/dist/jobs/chatter-queue-publisher.service.d.ts.map +1 -0
- package/dist/jobs/chatter-queue-publisher.service.js +39 -0
- package/dist/jobs/chatter-queue-publisher.service.js.map +1 -0
- package/dist/jobs/chatter-queue-subscriber.service.d.ts +17 -0
- package/dist/jobs/chatter-queue-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/chatter-queue-subscriber.service.js +59 -0
- package/dist/jobs/chatter-queue-subscriber.service.js.map +1 -0
- package/dist/jobs/{database/computed-field-evaluation-publisher.service.d.ts → computed-field-evaluation-publisher.service.d.ts} +2 -2
- package/dist/jobs/computed-field-evaluation-publisher.service.d.ts.map +1 -0
- package/dist/jobs/{database/computed-field-evaluation-publisher.service.js → computed-field-evaluation-publisher.service.js} +8 -8
- package/dist/jobs/computed-field-evaluation-publisher.service.js.map +1 -0
- package/dist/jobs/{database/computed-field-evaluation-queue-options.d.ts → computed-field-evaluation-queue-options.d.ts} +1 -1
- package/dist/jobs/computed-field-evaluation-queue-options.d.ts.map +1 -0
- package/dist/jobs/{database/computed-field-evaluation-queue-options.js → computed-field-evaluation-queue-options.js} +2 -2
- package/dist/jobs/computed-field-evaluation-queue-options.js.map +1 -0
- package/dist/jobs/{database/computed-field-evaluation-subscriber.service.d.ts → computed-field-evaluation-subscriber.service.d.ts} +3 -3
- package/dist/jobs/computed-field-evaluation-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/computed-field-evaluation-subscriber.service.js +51 -0
- package/dist/jobs/computed-field-evaluation-subscriber.service.js.map +1 -0
- package/dist/jobs/database/computed-field-evaluation-publisher-database.service.d.ts +12 -0
- package/dist/jobs/database/computed-field-evaluation-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/computed-field-evaluation-publisher-database.service.js +39 -0
- package/dist/jobs/database/computed-field-evaluation-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/computed-field-evaluation-queue-options-database.d.ts +8 -0
- package/dist/jobs/database/computed-field-evaluation-queue-options-database.d.ts.map +1 -0
- package/dist/jobs/database/computed-field-evaluation-queue-options-database.js +10 -0
- package/dist/jobs/database/computed-field-evaluation-queue-options-database.js.map +1 -0
- package/dist/jobs/database/computed-field-evaluation-subscriber-database.service.d.ts +18 -0
- package/dist/jobs/database/computed-field-evaluation-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/{computed-field-evaluation-subscriber.service.js → computed-field-evaluation-subscriber-database.service.js} +8 -8
- package/dist/jobs/database/computed-field-evaluation-subscriber-database.service.js.map +1 -0
- package/dist/jobs/database/generate-code-queue-options-database.js +2 -2
- package/dist/jobs/database/generate-code-queue-options-database.js.map +1 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.js +7 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.js.map +1 -1
- package/dist/jobs/generate-code-publisher.service.d.ts +11 -0
- package/dist/jobs/generate-code-publisher.service.d.ts.map +1 -0
- package/dist/jobs/generate-code-publisher.service.js +39 -0
- package/dist/jobs/generate-code-publisher.service.js.map +1 -0
- package/dist/jobs/generate-code-queue-options.d.ts +8 -0
- package/dist/jobs/generate-code-queue-options.d.ts.map +1 -0
- package/dist/jobs/generate-code-queue-options.js +10 -0
- package/dist/jobs/generate-code-queue-options.js.map +1 -0
- package/dist/jobs/generate-code-subscriber.service.d.ts +18 -0
- package/dist/jobs/generate-code-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/generate-code-subscriber.service.js +70 -0
- package/dist/jobs/generate-code-subscriber.service.js.map +1 -0
- package/dist/jobs/test-queue-subscriber.service.d.ts +1 -1
- package/dist/jobs/test-queue-subscriber.service.d.ts.map +1 -1
- package/dist/jobs/test-queue-subscriber.service.js +9 -6
- package/dist/jobs/test-queue-subscriber.service.js.map +1 -1
- package/dist/jobs/trigger-mcp-client-publisher.service.d.ts +11 -0
- package/dist/jobs/trigger-mcp-client-publisher.service.d.ts.map +1 -0
- package/dist/jobs/trigger-mcp-client-publisher.service.js +39 -0
- package/dist/jobs/trigger-mcp-client-publisher.service.js.map +1 -0
- package/dist/jobs/trigger-mcp-client-queue-options.d.ts +8 -0
- package/dist/jobs/trigger-mcp-client-queue-options.d.ts.map +1 -0
- package/dist/jobs/trigger-mcp-client-queue-options.js +10 -0
- package/dist/jobs/trigger-mcp-client-queue-options.js.map +1 -0
- package/dist/jobs/trigger-mcp-client-subscriber.service.d.ts +18 -0
- package/dist/jobs/trigger-mcp-client-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/trigger-mcp-client-subscriber.service.js +103 -0
- package/dist/jobs/trigger-mcp-client-subscriber.service.js.map +1 -0
- package/dist/jobs/twilio-sms-publisher.service.d.ts +11 -0
- package/dist/jobs/twilio-sms-publisher.service.d.ts.map +1 -0
- package/dist/jobs/twilio-sms-publisher.service.js +39 -0
- package/dist/jobs/twilio-sms-publisher.service.js.map +1 -0
- package/dist/jobs/twilio-sms-queue-options.d.ts +8 -0
- package/dist/jobs/twilio-sms-queue-options.d.ts.map +1 -0
- package/dist/jobs/twilio-sms-queue-options.js +10 -0
- package/dist/jobs/twilio-sms-queue-options.js.map +1 -0
- package/dist/jobs/twilio-sms-subscriber.service.d.ts +17 -0
- package/dist/jobs/twilio-sms-subscriber.service.d.ts.map +1 -0
- package/dist/jobs/twilio-sms-subscriber.service.js +48 -0
- package/dist/jobs/twilio-sms-subscriber.service.js.map +1 -0
- 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 +3 -3
- package/dist/services/authentication.service.js.map +1 -1
- package/dist/services/queues/database-publisher.service.js +2 -2
- package/dist/services/queues/database-publisher.service.js.map +1 -1
- package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/database-subscriber.service.js +2 -1
- package/dist/services/queues/database-subscriber.service.js.map +1 -1
- package/dist/services/queues/publisher-factory.service.js +1 -1
- package/dist/services/queues/publisher-factory.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 +23 -4
- package/dist/solid-core.module.js.map +1 -1
- package/dist/subscribers/audit.subscriber.d.ts +8 -0
- package/dist/subscribers/audit.subscriber.d.ts.map +1 -1
- package/dist/subscribers/audit.subscriber.js +52 -3
- package/dist/subscribers/audit.subscriber.js.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.d.ts +3 -3
- package/dist/subscribers/computed-entity-field.subscriber.d.ts.map +1 -1
- package/dist/subscribers/computed-entity-field.subscriber.js +5 -7
- package/dist/subscribers/computed-entity-field.subscriber.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/controllers/test-queue.controller.ts +4 -3
- package/src/decorators/error-codes-provider.decorator.ts +9 -0
- 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/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/jobs/chatter-queue-options.ts +9 -0
- package/src/jobs/chatter-queue-publisher.service.ts +37 -0
- package/src/jobs/chatter-queue-subscriber.service.ts +46 -0
- package/src/jobs/computed-field-evaluation-publisher.service.ts +23 -0
- package/src/jobs/{database/computed-field-evaluation-queue-options.ts → computed-field-evaluation-queue-options.ts} +2 -2
- package/src/jobs/computed-field-evaluation-subscriber.service.ts +38 -0
- package/src/jobs/database/{computed-field-evaluation-publisher.service.ts → computed-field-evaluation-publisher-database.service.ts} +2 -2
- package/src/jobs/database/computed-field-evaluation-queue-options-database.ts +9 -0
- package/src/jobs/database/{computed-field-evaluation-subscriber.service.ts → computed-field-evaluation-subscriber-database.service.ts} +2 -2
- package/src/jobs/database/generate-code-queue-options-database.ts +2 -2
- package/src/jobs/database/test-queue-subscriber-database.service.ts +10 -2
- package/src/jobs/generate-code-publisher.service.ts +23 -0
- package/src/jobs/generate-code-queue-options.ts +9 -0
- package/src/jobs/generate-code-subscriber.service.ts +59 -0
- package/src/jobs/test-queue-subscriber.service.ts +15 -7
- package/src/jobs/trigger-mcp-client-publisher.service.ts +22 -0
- package/src/jobs/trigger-mcp-client-queue-options.ts +9 -0
- package/src/jobs/trigger-mcp-client-subscriber.service.ts +104 -0
- package/src/jobs/twilio-sms-publisher.service.ts +23 -0
- package/src/jobs/twilio-sms-queue-options.ts +9 -0
- package/src/jobs/twilio-sms-subscriber.service.ts +32 -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 +4 -4
- package/src/services/queues/database-publisher.service.ts +2 -2
- package/src/services/queues/database-subscriber.service.ts +2 -1
- package/src/services/queues/publisher-factory.service.ts +1 -1
- package/src/services/solid-introspect.service.ts +22 -0
- package/src/solid-core.module.ts +35 -8
- package/src/subscribers/audit.subscriber.ts +235 -5
- package/src/subscribers/computed-entity-field.subscriber.ts +7 -5
- package/dist/jobs/database/computed-field-evaluation-publisher.service.d.ts.map +0 -1
- package/dist/jobs/database/computed-field-evaluation-publisher.service.js.map +0 -1
- package/dist/jobs/database/computed-field-evaluation-queue-options.d.ts.map +0 -1
- package/dist/jobs/database/computed-field-evaluation-queue-options.js.map +0 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.d.ts.map +0 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.166",
|
|
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",
|
|
@@ -18,14 +18,15 @@ export class TestQueueController {
|
|
|
18
18
|
) { }
|
|
19
19
|
|
|
20
20
|
@Public()
|
|
21
|
-
@Get(':messageBroker')
|
|
22
|
-
async getHello(@Param('messageBroker') messageBroker: string) {
|
|
21
|
+
@Get(':messageBroker/:timeoutSeconds')
|
|
22
|
+
async getHello(@Param('messageBroker') messageBroker: string, @Param('timeoutSeconds') timeoutSeconds: number) {
|
|
23
23
|
const pubsubMessage = 'A hopping-good time!';
|
|
24
24
|
const m = {
|
|
25
25
|
payload: {
|
|
26
26
|
firstName: 'Harish',
|
|
27
27
|
lastName: 'Patel',
|
|
28
|
-
age: 40
|
|
28
|
+
age: 40,
|
|
29
|
+
timeoutSeconds: timeoutSeconds
|
|
29
30
|
},
|
|
30
31
|
parentEntity: 'feeType',
|
|
31
32
|
parentEntityId: 23,
|
|
@@ -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
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// src/common/errors/providers/solidcore-error-code.provider.ts
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { ErrorCodeProvider } from 'src/decorators/error-codes-provider.decorator';
|
|
4
|
+
import { ErrorMeta, ErrorRule, IErrorCodeProvider } from 'src/interfaces';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@ErrorCodeProvider()
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class SolidCoreErrorCodesProvider implements IErrorCodeProvider {
|
|
10
|
+
name(): string {
|
|
11
|
+
return 'SolidCoreErrorCodeProvider';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
rules(): ReadonlyArray<ErrorRule> {
|
|
15
|
+
return [
|
|
16
|
+
{
|
|
17
|
+
code: 'solidx-mcp-server-unavailable',
|
|
18
|
+
priority: 100, // run early
|
|
19
|
+
match: (txt) =>
|
|
20
|
+
txt.includes('all connection attempts failed') &&
|
|
21
|
+
txt.includes('unhandled errors in a taskgroup (1 sub-exception)'),
|
|
22
|
+
meta: {
|
|
23
|
+
message: 'SolidX MCP server is unreachable. Please verify the MCP endpoint.',
|
|
24
|
+
httpStatus: 503,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
code: 'db-duplicate-key',
|
|
29
|
+
priority: 90,
|
|
30
|
+
match: (txt) => txt.includes('unique constraint') || txt.includes('duplicate key'),
|
|
31
|
+
meta: {
|
|
32
|
+
message: 'Duplicate key violation. A record with these values already exists.',
|
|
33
|
+
httpStatus: 409,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
code: 'db-foreign-key-error',
|
|
38
|
+
priority: 90,
|
|
39
|
+
match: (txt) => txt.includes('violates foreign key'),
|
|
40
|
+
meta: {
|
|
41
|
+
message:
|
|
42
|
+
'Foreign key constraint prevents this operation due to related records.',
|
|
43
|
+
httpStatus: 409,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
code: 'unknown-error',
|
|
48
|
+
priority: -1, // last resort
|
|
49
|
+
match: (_txt) => true, // fallback catch-all
|
|
50
|
+
meta: {
|
|
51
|
+
message: 'An unexpected error occurred.',
|
|
52
|
+
httpStatus: 500,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Optional explicit meta resolution (if you want)
|
|
59
|
+
resolve(code: string): ErrorMeta | undefined {
|
|
60
|
+
const rule = this.rules().find((r) => r.code === code);
|
|
61
|
+
return rule?.meta;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -5,7 +5,7 @@ import { CommonEntity } from 'src/entities/common.entity';
|
|
|
5
5
|
import { Locale } from 'src/entities/locale.entity';
|
|
6
6
|
import { SecurityRule } from 'src/entities/security-rule.entity';
|
|
7
7
|
import { IScheduledJob } from 'src/services/scheduled-jobs/scheduled-job.interface';
|
|
8
|
-
import { IDashboardQuestionDataProvider, IDashboardVariableSelectionProvider, ISelectionProvider, ISelectionProviderContext } from "../interfaces";
|
|
8
|
+
import { IDashboardQuestionDataProvider, IDashboardVariableSelectionProvider, IErrorCodeProvider, ISelectionProvider, ISelectionProviderContext } from "../interfaces";
|
|
9
9
|
|
|
10
10
|
type ControllerMetadata = {
|
|
11
11
|
name: string;
|
|
@@ -67,6 +67,12 @@ export class SolidRegistry {
|
|
|
67
67
|
private dashboardQuestionDataProviders: Set<InstanceWrapper> = new Set();
|
|
68
68
|
private mailProviders: Set<InstanceWrapper> = new Set();
|
|
69
69
|
private whatsappProviders: Set<InstanceWrapper> = new Set();
|
|
70
|
+
private errorCodeProviders: Set<InstanceWrapper> = new Set();
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
registerErrorCodeProvider(errorCodeProvider: InstanceWrapper): void {
|
|
74
|
+
this.errorCodeProviders.add(errorCodeProvider);
|
|
75
|
+
}
|
|
70
76
|
|
|
71
77
|
registerWhatsappProvider(whatsappProvider: InstanceWrapper): void {
|
|
72
78
|
this.whatsappProviders.add(whatsappProvider);
|
|
@@ -162,6 +168,19 @@ export class SolidRegistry {
|
|
|
162
168
|
}
|
|
163
169
|
}
|
|
164
170
|
|
|
171
|
+
getErrorCodeProviders(): Array<InstanceWrapper> {
|
|
172
|
+
return Array.from(this.errorCodeProviders);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
getErrorCodeProviderInstance(name: string): IErrorCodeProvider | undefined {
|
|
176
|
+
const providers = this.getErrorCodeProviders();
|
|
177
|
+
for (let i = 0; i < providers.length; i++) {
|
|
178
|
+
const p = providers[i];
|
|
179
|
+
if (p.instance?.name?.() === name) return p.instance as IErrorCodeProvider;
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
165
184
|
getDashboardQuestionDataProviders(): Array<InstanceWrapper> {
|
|
166
185
|
return Array.from(this.dashboardQuestionDataProviders)
|
|
167
186
|
}
|
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export * from './decorators/protocol.decorator'
|
|
|
19
19
|
export * from './decorators/public.decorator'
|
|
20
20
|
export * from './decorators/roles.decorator'
|
|
21
21
|
export * from './decorators/selection-provider.decorator'
|
|
22
|
+
export * from './decorators/error-codes-provider.decorator'
|
|
22
23
|
export * from './decorators/solid-database-module.decorator'
|
|
23
24
|
export * from './decorators/solid-service.decorator'
|
|
24
25
|
export * from './decorators/mail-provider.decorator'
|
package/src/interfaces.ts
CHANGED
|
@@ -238,3 +238,39 @@ export interface QueuesModuleOptions {
|
|
|
238
238
|
export type MediaWithFullUrl = Media & {
|
|
239
239
|
_full_url: string;
|
|
240
240
|
};
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
export type ErrorCode = string;
|
|
244
|
+
|
|
245
|
+
export type ErrorMeta = {
|
|
246
|
+
message: string;
|
|
247
|
+
httpStatus?: number;
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
export type ErrorRule = {
|
|
251
|
+
/** Canonical error code. Keep them kebab-case for consistency. */
|
|
252
|
+
code: ErrorCode;
|
|
253
|
+
/** Higher runs earlier. Defaults to 0 if not provided. */
|
|
254
|
+
priority?: number;
|
|
255
|
+
/** Return true if this rule matches the combined error text. */
|
|
256
|
+
match: (combinedErrorText: string) => boolean;
|
|
257
|
+
/** Display + HTTP mapping for this code. */
|
|
258
|
+
meta: ErrorMeta;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export interface IErrorCodeProvider {
|
|
262
|
+
/** Used for registry identity & logs */
|
|
263
|
+
name(): string;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Return all rules this provider contributes.
|
|
267
|
+
* These will be merged with other providers’ rules, then sorted by priority.
|
|
268
|
+
*/
|
|
269
|
+
rules(): ReadonlyArray<ErrorRule>;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Optional fallback meta for codes this provider owns (when called by getMessage/getHttpStatus).
|
|
273
|
+
* If omitted, the ErrorMapperService will rely on the rule.meta of the first matching rule.
|
|
274
|
+
*/
|
|
275
|
+
resolve?(code: ErrorCode): ErrorMeta | undefined;
|
|
276
|
+
}
|