@hiliosai/sdk 0.1.30 → 0.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/dist/index.d.ts +87 -2
- package/dist/index.js +596 -2
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Context, ServiceBroker, ServiceSchema as ServiceSchema$1, ServiceSettingSchema, Service, ActionSchema, ServiceEvents, ServiceHooks, Middleware, BrokerOptions } from 'moleculer';
|
|
1
|
+
import { Context, ServiceBroker, ServiceSchema as ServiceSchema$1, ServiceSettingSchema, Service, ActionSchema, ServiceEvents, ServiceHooks, Middleware, BrokerOptions, Errors, BrokerErrorHandlerInfoAction } from 'moleculer';
|
|
2
2
|
import env from '@ltv/env';
|
|
3
3
|
export { default as env } from '@ltv/env';
|
|
4
4
|
import { PrismaPg } from '@prisma/adapter-pg';
|
|
5
|
+
import { IncomingMessage, ServerResponse } from 'http';
|
|
5
6
|
|
|
6
7
|
interface Tenant {
|
|
7
8
|
id: string;
|
|
@@ -898,6 +899,90 @@ type Env = typeof env;
|
|
|
898
899
|
*/
|
|
899
900
|
declare function DatasourceMixin(datasourceConstructors?: DatasourceConstructorRegistry): Partial<ServiceSchema$1>;
|
|
900
901
|
|
|
902
|
+
/**
|
|
903
|
+
* Determines the appropriate log level based on error code/status
|
|
904
|
+
*/
|
|
905
|
+
declare function getErrorLogLevel(code?: string | number): 'fatal' | 'error' | 'warn' | 'info';
|
|
906
|
+
/**
|
|
907
|
+
* Sanitizes error data to remove sensitive information
|
|
908
|
+
*/
|
|
909
|
+
declare function sanitizeErrorData(data: unknown): unknown;
|
|
910
|
+
|
|
901
911
|
declare function omit<T extends Record<string, unknown>, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
|
|
902
912
|
|
|
903
|
-
|
|
913
|
+
declare class PermissionError extends Error {
|
|
914
|
+
readonly code = "PERMISSION_DENIED";
|
|
915
|
+
readonly statusCode = 403;
|
|
916
|
+
readonly data: Record<string, unknown> | undefined;
|
|
917
|
+
constructor(message: string, data?: Record<string, unknown>);
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
declare class AuthenticationError extends Error {
|
|
921
|
+
readonly code = "AUTH_REQUIRED";
|
|
922
|
+
readonly statusCode = 401;
|
|
923
|
+
readonly data: Record<string, unknown> | undefined;
|
|
924
|
+
constructor(message: string, data?: Record<string, unknown>);
|
|
925
|
+
}
|
|
926
|
+
declare class TenantError extends Error {
|
|
927
|
+
readonly code = "TENANT_REQUIRED";
|
|
928
|
+
readonly statusCode = 401;
|
|
929
|
+
readonly data: Record<string, unknown> | undefined;
|
|
930
|
+
constructor(message: string, data?: Record<string, unknown>);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
interface GatewayError extends Error {
|
|
934
|
+
code?: string | number;
|
|
935
|
+
statusCode?: number;
|
|
936
|
+
type?: string;
|
|
937
|
+
data?: any;
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Custom error handler for the API Gateway
|
|
941
|
+
* Formats errors based on enterprise standards (Stripe, Google Cloud, AWS)
|
|
942
|
+
*
|
|
943
|
+
* Example response:
|
|
944
|
+
* {
|
|
945
|
+
* "error": {
|
|
946
|
+
* "code": "UNIQUE_VIOLATION",
|
|
947
|
+
* "message": "Unique constraint violation",
|
|
948
|
+
* "details": [{
|
|
949
|
+
* "field": "email",
|
|
950
|
+
* "constraint": "unique",
|
|
951
|
+
* "reason": "The value must be unique"
|
|
952
|
+
* }],
|
|
953
|
+
* "timestamp": "2024-01-27T10:30:00Z",
|
|
954
|
+
* "path": "/api/users",
|
|
955
|
+
* "requestId": "req_abc123",
|
|
956
|
+
* "help": "/docs/errors#unique-constraint"
|
|
957
|
+
* }
|
|
958
|
+
* }
|
|
959
|
+
*/
|
|
960
|
+
declare function gatewayErrorHandler(this: Service, req: IncomingMessage & {
|
|
961
|
+
$ctx?: AppContext;
|
|
962
|
+
originalUrl?: string;
|
|
963
|
+
}, res: ServerResponse, error: GatewayError): void;
|
|
964
|
+
|
|
965
|
+
declare class DBError extends Errors.MoleculerError {
|
|
966
|
+
constructor(message: string, code: number, type?: string, data?: unknown);
|
|
967
|
+
}
|
|
968
|
+
declare class DBValidationError extends Errors.MoleculerError {
|
|
969
|
+
constructor(message: string, type?: string, data?: unknown, code?: number);
|
|
970
|
+
}
|
|
971
|
+
declare class DBConstraintError extends Errors.MoleculerError {
|
|
972
|
+
constructor(message: string, type?: string, data?: unknown);
|
|
973
|
+
}
|
|
974
|
+
declare class DBNotFoundError extends Errors.MoleculerError {
|
|
975
|
+
constructor(message: string, data?: unknown);
|
|
976
|
+
}
|
|
977
|
+
declare class DBConnectionError extends Errors.MoleculerError {
|
|
978
|
+
constructor(message: string, data?: unknown);
|
|
979
|
+
}
|
|
980
|
+
declare class DBTimeoutError extends Errors.MoleculerError {
|
|
981
|
+
constructor(message: string, data?: unknown);
|
|
982
|
+
}
|
|
983
|
+
declare class DBTransactionError extends Errors.MoleculerError {
|
|
984
|
+
constructor(message: string, data?: unknown);
|
|
985
|
+
}
|
|
986
|
+
declare function prismaErrorHandler(this: ServiceBroker, err: Error, info: BrokerErrorHandlerInfoAction): undefined;
|
|
987
|
+
|
|
988
|
+
export { AbstractDatasource, type ActionHandler, type ActionWithPermissions, type AppContext, type AppMeta, type AuditTrailExtension, AuthenticationError, type BaseDatasource, type BaseSpec, CHANNELS, type CarouselItem, type ChannelSendOptions, ContextHelpersMiddleware, CreateHealthCheckMiddleware, DBConnectionError, DBConstraintError, DBError, DBNotFoundError, DBTimeoutError, DBTransactionError, DBValidationError, DEFAULT_DATASOURCE_CACHE_TTL, type DatasourceConstructorRegistry, type DatasourceContext, type DatasourceInstanceRegistry, type DatasourceInstanceTypes, DatasourceMixin, type Env, type GatewayError, HEALTH_CHECK_DEFAULTS, INTEGRATION_CHANNELS, IntegrationCapability, type IntegrationChannelName, type IntegrationConfig, type IntegrationMessageFailedPayload, type IntegrationMessageReceivedPayload, type IntegrationMessageSentPayload, IntegrationPlatform, type IntegrationRegisteredPayload, type IntegrationServiceConfig, type IntegrationServiceSchema, IntegrationStatus, type IntegrationUnregisteredPayload, MemoizeMixin, type MemoizeMixinOptions, type Message, type MessageAttachment, type MessageButton, type MessageContent, MessageContentType, type MessageDirection, type MessageParticipant, type MessageStatus, type MessageType, NAMESPACE, PERMISSIONS, type Permission, PermissionError, type PermissionHelpers, PermissionsMiddleware, type PlatformMessage, type PrismaClientLike, type PrismaClientWithTenant, PrismaDatasource, PrismaPgDatasource, REDIS_URL, ROLE_PERMISSIONS, type SendResult, type SendToChannelMethod, type ServiceActionsSchema, type ServiceConfig, type ServiceSchema, type ServiceWithDatasources, type SoftDeleteExtension, type Tenant, TenantError, type TenantExtension, type User, UserRole, type WebhookEvent, createDatasourceMiddleware, createTenantExtension, defineIntegration, defineService, gatewayErrorHandler, getErrorLogLevel, isDev, isProd, isTest, configs as moleculer, nodeEnv, omit, prismaErrorHandler, retryExtension, sanitizeErrorData, softDeleteExtension };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import env4 from '@ltv/env';
|
|
2
2
|
import http from 'http';
|
|
3
|
+
import { PrismaClientValidationError, PrismaClientInitializationError, PrismaClientUnknownRequestError, PrismaClientRustPanicError, PrismaClientKnownRequestError } from '@prisma/client/runtime/client';
|
|
4
|
+
import { Errors } from 'moleculer';
|
|
3
5
|
import crypto from 'crypto';
|
|
4
6
|
import os from 'os';
|
|
5
7
|
import { Middleware } from '@moleculer/channels';
|
|
@@ -297,6 +299,503 @@ var TenantError = class _TenantError extends Error {
|
|
|
297
299
|
}
|
|
298
300
|
};
|
|
299
301
|
|
|
302
|
+
// src/errors/gateway.error.ts
|
|
303
|
+
function gatewayErrorHandler(req, res, error) {
|
|
304
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
305
|
+
const statusCode = typeof error.code === "number" ? error.code : typeof error.statusCode === "number" ? error.statusCode : 500;
|
|
306
|
+
res.writeHead(statusCode);
|
|
307
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
308
|
+
const requestId = req.$ctx?.id ?? req.$ctx?.requestID;
|
|
309
|
+
const path = req.originalUrl ?? req.url;
|
|
310
|
+
const docLinks = {
|
|
311
|
+
UNIQUE_VIOLATION: "/docs/errors#unique-constraint",
|
|
312
|
+
FOREIGN_KEY_VIOLATION: "/docs/errors#foreign-key",
|
|
313
|
+
VALIDATION_ERROR: "/docs/errors#validation",
|
|
314
|
+
NOT_FOUND: "/docs/errors#not-found",
|
|
315
|
+
MISSING_REQUIRED_VALUE: "/docs/errors#required-fields",
|
|
316
|
+
VALUE_TOO_LONG: "/docs/errors#field-length",
|
|
317
|
+
VALUE_OUT_OF_RANGE: "/docs/errors#value-range",
|
|
318
|
+
DB_CONNECTION_ERROR: "/docs/errors#connection",
|
|
319
|
+
DB_TIMEOUT: "/docs/errors#timeout",
|
|
320
|
+
TRANSACTION_ERROR: "/docs/errors#transaction"
|
|
321
|
+
};
|
|
322
|
+
const details = [];
|
|
323
|
+
if (error.data?.field) {
|
|
324
|
+
details.push({
|
|
325
|
+
field: error.data.field,
|
|
326
|
+
constraint: error.data.constraint,
|
|
327
|
+
reason: error.data.reason ?? error.message,
|
|
328
|
+
value: error.data.value
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
if (error.data?.errors && Array.isArray(error.data.errors)) {
|
|
332
|
+
error.data.errors.forEach((e) => {
|
|
333
|
+
details.push({
|
|
334
|
+
field: e.field ?? e.path,
|
|
335
|
+
reason: e.message ?? e.reason,
|
|
336
|
+
constraint: e.code ?? e.type
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
const errorCode = error.type ?? (error.name || "INTERNAL_ERROR");
|
|
341
|
+
const response = {
|
|
342
|
+
error: {
|
|
343
|
+
code: errorCode,
|
|
344
|
+
message: error.message || "An unexpected error occurred",
|
|
345
|
+
...details.length > 0 && { details },
|
|
346
|
+
timestamp,
|
|
347
|
+
...path && { path },
|
|
348
|
+
...requestId && { requestId },
|
|
349
|
+
...docLinks[errorCode] && { help: docLinks[errorCode] }
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "info";
|
|
353
|
+
this.logger[logLevel]("API Gateway Error", {
|
|
354
|
+
code: errorCode,
|
|
355
|
+
statusCode,
|
|
356
|
+
message: error.message,
|
|
357
|
+
requestId,
|
|
358
|
+
path,
|
|
359
|
+
method: req.method,
|
|
360
|
+
timestamp,
|
|
361
|
+
details: details.length > 0 ? details : void 0,
|
|
362
|
+
stack: statusCode >= 500 ? error.stack : void 0,
|
|
363
|
+
data: error.data
|
|
364
|
+
});
|
|
365
|
+
res.end(JSON.stringify(response));
|
|
366
|
+
}
|
|
367
|
+
var DBError = class extends Errors.MoleculerError {
|
|
368
|
+
constructor(message, code, type = "DB_ERROR", data) {
|
|
369
|
+
super(message, code, type, data);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
var DBValidationError = class extends Errors.MoleculerError {
|
|
373
|
+
constructor(message, type = "VALIDATION_ERROR", data, code = 422) {
|
|
374
|
+
super(message, code, type, data);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
var DBConstraintError = class extends Errors.MoleculerError {
|
|
378
|
+
constructor(message, type = "CONSTRAINT_ERROR", data) {
|
|
379
|
+
super(message, 409, type, data);
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
var DBNotFoundError = class extends Errors.MoleculerError {
|
|
383
|
+
constructor(message, data) {
|
|
384
|
+
super(message, 404, "NOT_FOUND", data);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
var DBConnectionError = class extends Errors.MoleculerError {
|
|
388
|
+
constructor(message, data) {
|
|
389
|
+
super(message, 503, "DB_CONNECTION_ERROR", data);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
var DBTimeoutError = class extends Errors.MoleculerError {
|
|
393
|
+
constructor(message, data) {
|
|
394
|
+
super(message, 504, "DB_TIMEOUT", data);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
var DBTransactionError = class extends Errors.MoleculerError {
|
|
398
|
+
constructor(message, data) {
|
|
399
|
+
super(message, 500, "TRANSACTION_ERROR", data);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
function logPrismaError(ctx, errorType, errorCode, message, meta) {
|
|
403
|
+
const logLevel = typeof errorCode === "number" && errorCode >= 500 ? "error" : "warn";
|
|
404
|
+
this.logger[logLevel]("Database Error", {
|
|
405
|
+
type: errorType,
|
|
406
|
+
code: errorCode,
|
|
407
|
+
message,
|
|
408
|
+
requestId: ctx.id,
|
|
409
|
+
service: this.name,
|
|
410
|
+
action: ctx.action?.name,
|
|
411
|
+
meta,
|
|
412
|
+
caller: ctx.caller
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
var prismaErrorHandlers = {
|
|
416
|
+
P2002(err, ctx) {
|
|
417
|
+
const errorType = "UNIQUE_VIOLATION";
|
|
418
|
+
const message = "Unique constraint violation";
|
|
419
|
+
const data = {
|
|
420
|
+
field: err.meta?.target ?? err.meta?.field_name,
|
|
421
|
+
constraint: "unique",
|
|
422
|
+
reason: `The value must be unique`,
|
|
423
|
+
prismaCode: err.code,
|
|
424
|
+
originalError: err.message,
|
|
425
|
+
requestId: ctx.id
|
|
426
|
+
};
|
|
427
|
+
logPrismaError.bind(this)(ctx, errorType, 409, message, data);
|
|
428
|
+
throw new DBConstraintError(message, errorType, data);
|
|
429
|
+
},
|
|
430
|
+
P2003(err, ctx) {
|
|
431
|
+
const errorType = "FOREIGN_KEY_VIOLATION";
|
|
432
|
+
const message = "Foreign key constraint failed";
|
|
433
|
+
const data = {
|
|
434
|
+
field: err.meta?.field_name,
|
|
435
|
+
original: err.message,
|
|
436
|
+
ctx: ctx.id
|
|
437
|
+
};
|
|
438
|
+
logPrismaError.bind(this)(ctx, errorType, 409, message, data);
|
|
439
|
+
throw new DBConstraintError(message, errorType, data);
|
|
440
|
+
},
|
|
441
|
+
P2014(err, ctx) {
|
|
442
|
+
throw new DBConstraintError(
|
|
443
|
+
"Invalid relation reference",
|
|
444
|
+
"INVALID_RELATION",
|
|
445
|
+
{
|
|
446
|
+
relation: err.meta?.relation_name,
|
|
447
|
+
original: err.message,
|
|
448
|
+
ctx: ctx.id
|
|
449
|
+
}
|
|
450
|
+
);
|
|
451
|
+
},
|
|
452
|
+
P2000(err, ctx) {
|
|
453
|
+
throw new DBValidationError("Value too long for column", "VALUE_TOO_LONG", {
|
|
454
|
+
column: err.meta?.column_name,
|
|
455
|
+
original: err.message,
|
|
456
|
+
ctx: ctx.id
|
|
457
|
+
});
|
|
458
|
+
},
|
|
459
|
+
P2001(err, ctx) {
|
|
460
|
+
throw new DBNotFoundError("Record not found in where condition", {
|
|
461
|
+
model: err.meta?.model_name,
|
|
462
|
+
original: err.message,
|
|
463
|
+
ctx: ctx.id
|
|
464
|
+
});
|
|
465
|
+
},
|
|
466
|
+
P2004(err, ctx) {
|
|
467
|
+
throw new DBConstraintError(
|
|
468
|
+
"Database constraint failed",
|
|
469
|
+
"CONSTRAINT_FAILED",
|
|
470
|
+
{
|
|
471
|
+
constraint: err.meta?.constraint,
|
|
472
|
+
original: err.message,
|
|
473
|
+
ctx: ctx.id
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
},
|
|
477
|
+
P2021(err, ctx) {
|
|
478
|
+
throw new DBError("Table does not exist", 500, "TABLE_NOT_FOUND", {
|
|
479
|
+
table: err.meta?.table,
|
|
480
|
+
original: err.message,
|
|
481
|
+
ctx: ctx.id
|
|
482
|
+
});
|
|
483
|
+
},
|
|
484
|
+
P2022(err, ctx) {
|
|
485
|
+
throw new DBError("Column does not exist", 500, "COLUMN_NOT_FOUND", {
|
|
486
|
+
column: err.meta?.column,
|
|
487
|
+
original: err.message,
|
|
488
|
+
ctx: ctx.id
|
|
489
|
+
});
|
|
490
|
+
},
|
|
491
|
+
P2006(err, ctx) {
|
|
492
|
+
throw new DBValidationError("Invalid value for field", "INVALID_VALUE", {
|
|
493
|
+
field: err.meta?.field_name,
|
|
494
|
+
original: err.message,
|
|
495
|
+
ctx: ctx.id
|
|
496
|
+
});
|
|
497
|
+
},
|
|
498
|
+
P2007(err, ctx) {
|
|
499
|
+
throw new DBValidationError(
|
|
500
|
+
"Data validation error",
|
|
501
|
+
"DATA_VALIDATION_ERROR",
|
|
502
|
+
{
|
|
503
|
+
original: err.message,
|
|
504
|
+
ctx: ctx.id
|
|
505
|
+
}
|
|
506
|
+
);
|
|
507
|
+
},
|
|
508
|
+
P2008(err, ctx) {
|
|
509
|
+
throw new DBError("Failed to parse the query", 400, "QUERY_PARSE_ERROR", {
|
|
510
|
+
original: err.message,
|
|
511
|
+
ctx: ctx.id
|
|
512
|
+
});
|
|
513
|
+
},
|
|
514
|
+
P2009(err, ctx) {
|
|
515
|
+
throw new DBError(
|
|
516
|
+
"Failed to validate the query",
|
|
517
|
+
400,
|
|
518
|
+
"QUERY_VALIDATION_ERROR",
|
|
519
|
+
{
|
|
520
|
+
original: err.message,
|
|
521
|
+
ctx: ctx.id
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
},
|
|
525
|
+
P2010(err, ctx) {
|
|
526
|
+
throw new DBError("Raw query failed", 500, "RAW_QUERY_FAILED", {
|
|
527
|
+
code: err.meta?.code,
|
|
528
|
+
original: err.message,
|
|
529
|
+
ctx: ctx.id
|
|
530
|
+
});
|
|
531
|
+
},
|
|
532
|
+
P2011(err, ctx) {
|
|
533
|
+
throw new DBConstraintError(
|
|
534
|
+
"Null constraint violation",
|
|
535
|
+
"NULL_CONSTRAINT_VIOLATION",
|
|
536
|
+
{
|
|
537
|
+
constraint: err.meta?.constraint,
|
|
538
|
+
original: err.message,
|
|
539
|
+
ctx: ctx.id
|
|
540
|
+
}
|
|
541
|
+
);
|
|
542
|
+
},
|
|
543
|
+
P2012(err, ctx) {
|
|
544
|
+
throw new DBValidationError(
|
|
545
|
+
"Missing a required value",
|
|
546
|
+
"MISSING_REQUIRED_VALUE",
|
|
547
|
+
{
|
|
548
|
+
field: err.meta?.field_name,
|
|
549
|
+
original: err.message,
|
|
550
|
+
ctx: ctx.id
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
},
|
|
554
|
+
P2013(err, ctx) {
|
|
555
|
+
throw new DBValidationError(
|
|
556
|
+
"Missing the required argument",
|
|
557
|
+
"MISSING_REQUIRED_ARGUMENT",
|
|
558
|
+
{
|
|
559
|
+
argument: err.meta?.argument_name,
|
|
560
|
+
original: err.message,
|
|
561
|
+
ctx: ctx.id
|
|
562
|
+
}
|
|
563
|
+
);
|
|
564
|
+
},
|
|
565
|
+
P2015(err, ctx) {
|
|
566
|
+
throw new DBNotFoundError("Related record not found", {
|
|
567
|
+
original: err.message,
|
|
568
|
+
ctx: ctx.id
|
|
569
|
+
});
|
|
570
|
+
},
|
|
571
|
+
P2016(err, ctx) {
|
|
572
|
+
throw new DBError(
|
|
573
|
+
"Query interpretation error",
|
|
574
|
+
400,
|
|
575
|
+
"QUERY_INTERPRETATION_ERROR",
|
|
576
|
+
{
|
|
577
|
+
original: err.message,
|
|
578
|
+
ctx: ctx.id
|
|
579
|
+
}
|
|
580
|
+
);
|
|
581
|
+
},
|
|
582
|
+
P2017(err, ctx) {
|
|
583
|
+
throw new DBConstraintError(
|
|
584
|
+
"Records relation violation",
|
|
585
|
+
"RELATION_VIOLATION",
|
|
586
|
+
{
|
|
587
|
+
relation: err.meta?.relation_name,
|
|
588
|
+
original: err.message,
|
|
589
|
+
ctx: ctx.id
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
},
|
|
593
|
+
P2018(err, ctx) {
|
|
594
|
+
throw new DBNotFoundError("Required connected records not found", {
|
|
595
|
+
original: err.message,
|
|
596
|
+
ctx: ctx.id
|
|
597
|
+
});
|
|
598
|
+
},
|
|
599
|
+
P2019(err, ctx) {
|
|
600
|
+
throw new DBValidationError("Input error", "INPUT_ERROR", {
|
|
601
|
+
original: err.message,
|
|
602
|
+
ctx: ctx.id
|
|
603
|
+
});
|
|
604
|
+
},
|
|
605
|
+
P2020(err, ctx) {
|
|
606
|
+
throw new DBValidationError("Value out of range", "VALUE_OUT_OF_RANGE", {
|
|
607
|
+
original: err.message,
|
|
608
|
+
ctx: ctx.id
|
|
609
|
+
});
|
|
610
|
+
},
|
|
611
|
+
P2023(err, ctx) {
|
|
612
|
+
throw new DBError(
|
|
613
|
+
"Inconsistent column data",
|
|
614
|
+
500,
|
|
615
|
+
"INCONSISTENT_COLUMN_DATA",
|
|
616
|
+
{
|
|
617
|
+
original: err.message,
|
|
618
|
+
ctx: ctx.id
|
|
619
|
+
}
|
|
620
|
+
);
|
|
621
|
+
},
|
|
622
|
+
P2024(err, ctx) {
|
|
623
|
+
const message = "Operation timed out";
|
|
624
|
+
const data = {
|
|
625
|
+
original: err.message,
|
|
626
|
+
ctx: ctx.id
|
|
627
|
+
};
|
|
628
|
+
logPrismaError.bind(this)(ctx, "DB_TIMEOUT", 504, message, data);
|
|
629
|
+
throw new DBTimeoutError(message, data);
|
|
630
|
+
},
|
|
631
|
+
P2025(err, ctx) {
|
|
632
|
+
throw new DBNotFoundError("Record not found for required operation", {
|
|
633
|
+
operation: err.meta?.operation_name,
|
|
634
|
+
model: err.meta?.model_name,
|
|
635
|
+
original: err.message,
|
|
636
|
+
ctx: ctx.id
|
|
637
|
+
});
|
|
638
|
+
},
|
|
639
|
+
P2026(err, ctx) {
|
|
640
|
+
throw new DBError("Unsupported feature", 501, "UNSUPPORTED_FEATURE", {
|
|
641
|
+
feature: err.meta?.feature,
|
|
642
|
+
original: err.message,
|
|
643
|
+
ctx: ctx.id
|
|
644
|
+
});
|
|
645
|
+
},
|
|
646
|
+
P2027(err, ctx) {
|
|
647
|
+
throw new DBError(
|
|
648
|
+
"Multiple database errors during execution",
|
|
649
|
+
500,
|
|
650
|
+
"MULTIPLE_DB_ERRORS",
|
|
651
|
+
{
|
|
652
|
+
original: err.message,
|
|
653
|
+
ctx: ctx.id
|
|
654
|
+
}
|
|
655
|
+
);
|
|
656
|
+
},
|
|
657
|
+
P2028(err, ctx) {
|
|
658
|
+
throw new DBTransactionError("Transaction API error", {
|
|
659
|
+
original: err.message,
|
|
660
|
+
ctx: ctx.id
|
|
661
|
+
});
|
|
662
|
+
},
|
|
663
|
+
P2030(err, ctx) {
|
|
664
|
+
throw new DBError(
|
|
665
|
+
"Cannot find fulltext index",
|
|
666
|
+
500,
|
|
667
|
+
"FULLTEXT_INDEX_NOT_FOUND",
|
|
668
|
+
{
|
|
669
|
+
original: err.message,
|
|
670
|
+
ctx: ctx.id
|
|
671
|
+
}
|
|
672
|
+
);
|
|
673
|
+
},
|
|
674
|
+
P2033(err, ctx) {
|
|
675
|
+
throw new DBValidationError("Number out of range", "NUMBER_OUT_OF_RANGE", {
|
|
676
|
+
field: err.meta?.field_name,
|
|
677
|
+
original: err.message,
|
|
678
|
+
ctx: ctx.id
|
|
679
|
+
});
|
|
680
|
+
},
|
|
681
|
+
P2034(err, ctx) {
|
|
682
|
+
throw new DBTransactionError("Transaction conflict", {
|
|
683
|
+
original: err.message,
|
|
684
|
+
ctx: ctx.id
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
function prismaErrorHandler(err, info) {
|
|
689
|
+
const { ctx, action } = info;
|
|
690
|
+
if (err instanceof PrismaClientValidationError) {
|
|
691
|
+
const errorType = "INVALID_INPUT_DATA";
|
|
692
|
+
const message = "Invalid input data";
|
|
693
|
+
const data = {
|
|
694
|
+
original: err.message,
|
|
695
|
+
ctx: ctx.id,
|
|
696
|
+
service: this.name,
|
|
697
|
+
action: action?.name
|
|
698
|
+
};
|
|
699
|
+
this.logger.warn("Database Validation Error", {
|
|
700
|
+
type: errorType,
|
|
701
|
+
message,
|
|
702
|
+
requestId: ctx.id,
|
|
703
|
+
service: this.name,
|
|
704
|
+
action: action?.name,
|
|
705
|
+
error: err.message
|
|
706
|
+
});
|
|
707
|
+
throw new DBValidationError(message, errorType, data);
|
|
708
|
+
}
|
|
709
|
+
if (err instanceof PrismaClientInitializationError) {
|
|
710
|
+
const message = "Database initialization failed";
|
|
711
|
+
const data = {
|
|
712
|
+
original: err.message,
|
|
713
|
+
ctx: ctx.id,
|
|
714
|
+
service: this.name,
|
|
715
|
+
action: action?.name
|
|
716
|
+
};
|
|
717
|
+
this.logger.error("Database Connection Error", {
|
|
718
|
+
type: "DB_CONNECTION_ERROR",
|
|
719
|
+
message,
|
|
720
|
+
requestId: ctx.id,
|
|
721
|
+
service: this.name,
|
|
722
|
+
action: action?.name,
|
|
723
|
+
error: err.message
|
|
724
|
+
});
|
|
725
|
+
throw new DBConnectionError(message, data);
|
|
726
|
+
}
|
|
727
|
+
if (err instanceof PrismaClientUnknownRequestError) {
|
|
728
|
+
const errorType = "DB_UNKNOWN_ERROR";
|
|
729
|
+
const message = "Unknown database error";
|
|
730
|
+
const data = {
|
|
731
|
+
original: err.message,
|
|
732
|
+
ctx: ctx.id,
|
|
733
|
+
service: this.name,
|
|
734
|
+
action: action?.name
|
|
735
|
+
};
|
|
736
|
+
this.logger.error("Unknown Database Error", {
|
|
737
|
+
type: errorType,
|
|
738
|
+
message,
|
|
739
|
+
requestId: ctx.id,
|
|
740
|
+
service: this.name,
|
|
741
|
+
action: action?.name,
|
|
742
|
+
error: err.message,
|
|
743
|
+
stack: err.stack
|
|
744
|
+
});
|
|
745
|
+
throw new DBError(message, 500, errorType, data);
|
|
746
|
+
}
|
|
747
|
+
if (err instanceof PrismaClientRustPanicError) {
|
|
748
|
+
const errorType = "DB_ENGINE_PANIC";
|
|
749
|
+
const message = "Database engine panic";
|
|
750
|
+
const data = {
|
|
751
|
+
original: err.message,
|
|
752
|
+
ctx: ctx.id,
|
|
753
|
+
service: this.name,
|
|
754
|
+
action: action?.name
|
|
755
|
+
};
|
|
756
|
+
this.logger.fatal("Database Engine Panic", {
|
|
757
|
+
type: errorType,
|
|
758
|
+
message,
|
|
759
|
+
requestId: ctx.id,
|
|
760
|
+
service: this.name,
|
|
761
|
+
action: action?.name,
|
|
762
|
+
error: err.message,
|
|
763
|
+
stack: err.stack
|
|
764
|
+
});
|
|
765
|
+
throw new DBError(message, 500, errorType, data);
|
|
766
|
+
}
|
|
767
|
+
if (err instanceof PrismaClientKnownRequestError) {
|
|
768
|
+
const handler = prismaErrorHandlers[err.code];
|
|
769
|
+
if (handler) {
|
|
770
|
+
return handler.bind(this)(err, ctx);
|
|
771
|
+
}
|
|
772
|
+
const errorType = "DB_CONSTRAINT_ERROR";
|
|
773
|
+
const message = "Unknown database constraint error";
|
|
774
|
+
const data = {
|
|
775
|
+
code: err.code,
|
|
776
|
+
meta: err.meta,
|
|
777
|
+
original: err.message,
|
|
778
|
+
ctx: ctx.id,
|
|
779
|
+
service: this.name,
|
|
780
|
+
action: action?.name
|
|
781
|
+
};
|
|
782
|
+
this.logger.error("Unknown Database Constraint Error", {
|
|
783
|
+
type: errorType,
|
|
784
|
+
prismaCode: err.code,
|
|
785
|
+
message,
|
|
786
|
+
requestId: ctx.id,
|
|
787
|
+
service: this.name,
|
|
788
|
+
action: action?.name,
|
|
789
|
+
meta: err.meta,
|
|
790
|
+
error: err.message
|
|
791
|
+
});
|
|
792
|
+
throw new DBError(message, 500, errorType, data);
|
|
793
|
+
}
|
|
794
|
+
if (err instanceof Errors.MoleculerError) {
|
|
795
|
+
throw err;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
300
799
|
// src/middlewares/permissions.middleware.ts
|
|
301
800
|
var permissionHandlers = {
|
|
302
801
|
[PERMISSIONS.AUTHENTICATED]: async (ctx) => !!ctx.meta.user?.id,
|
|
@@ -654,6 +1153,52 @@ function DatasourceMixin(datasourceConstructors = {}) {
|
|
|
654
1153
|
};
|
|
655
1154
|
}
|
|
656
1155
|
|
|
1156
|
+
// src/utils/error-logger.ts
|
|
1157
|
+
function getErrorLogLevel(code) {
|
|
1158
|
+
if (typeof code === "number") {
|
|
1159
|
+
if (code >= 500) return "error";
|
|
1160
|
+
if (code >= 400) return "warn";
|
|
1161
|
+
return "info";
|
|
1162
|
+
}
|
|
1163
|
+
if (typeof code === "string") {
|
|
1164
|
+
const criticalErrors = [
|
|
1165
|
+
"DB_ENGINE_PANIC",
|
|
1166
|
+
"SYSTEM_FAILURE",
|
|
1167
|
+
"CRITICAL_ERROR"
|
|
1168
|
+
];
|
|
1169
|
+
if (criticalErrors.includes(code)) return "fatal";
|
|
1170
|
+
const systemErrors = [
|
|
1171
|
+
"DB_CONNECTION_ERROR",
|
|
1172
|
+
"INTERNAL_ERROR",
|
|
1173
|
+
"TRANSACTION_ERROR"
|
|
1174
|
+
];
|
|
1175
|
+
if (systemErrors.includes(code)) return "error";
|
|
1176
|
+
}
|
|
1177
|
+
return "warn";
|
|
1178
|
+
}
|
|
1179
|
+
function sanitizeErrorData(data) {
|
|
1180
|
+
if (!data) return data;
|
|
1181
|
+
const sensitiveKeys = [
|
|
1182
|
+
"password",
|
|
1183
|
+
"token",
|
|
1184
|
+
"secret",
|
|
1185
|
+
"apiKey",
|
|
1186
|
+
"authorization"
|
|
1187
|
+
];
|
|
1188
|
+
if (typeof data === "object") {
|
|
1189
|
+
const sanitized = Array.isArray(data) ? [...data] : { ...data };
|
|
1190
|
+
for (const key in sanitized) {
|
|
1191
|
+
if (sensitiveKeys.some((k) => key.toLowerCase().includes(k.toLowerCase()))) {
|
|
1192
|
+
sanitized[key] = "[REDACTED]";
|
|
1193
|
+
} else if (typeof sanitized[key] === "object") {
|
|
1194
|
+
sanitized[key] = sanitizeErrorData(sanitized[key]);
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
return sanitized;
|
|
1198
|
+
}
|
|
1199
|
+
return data;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
657
1202
|
// src/utils/index.ts
|
|
658
1203
|
function omit(obj, keys) {
|
|
659
1204
|
const result = { ...obj };
|
|
@@ -1434,7 +1979,56 @@ var configs = {
|
|
|
1434
1979
|
ChannelsMiddleware,
|
|
1435
1980
|
PermissionsMiddleware,
|
|
1436
1981
|
ContextHelpersMiddleware
|
|
1437
|
-
]
|
|
1982
|
+
],
|
|
1983
|
+
errorHandler(err, info) {
|
|
1984
|
+
const { ctx, action } = info;
|
|
1985
|
+
try {
|
|
1986
|
+
prismaErrorHandler.bind(this)(err, info);
|
|
1987
|
+
} catch (handledError) {
|
|
1988
|
+
if (handledError instanceof Errors.MoleculerError) {
|
|
1989
|
+
throw handledError;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
const statusCode = err.code ?? err.statusCode ?? 500;
|
|
1993
|
+
const errorType = err.type ?? "INTERNAL_ERROR";
|
|
1994
|
+
const isSystemError = statusCode >= 500;
|
|
1995
|
+
const logData = {
|
|
1996
|
+
type: errorType,
|
|
1997
|
+
message: err.message,
|
|
1998
|
+
requestId: ctx.id,
|
|
1999
|
+
service: this.name,
|
|
2000
|
+
action: action?.name,
|
|
2001
|
+
params: ctx.params,
|
|
2002
|
+
caller: ctx.caller,
|
|
2003
|
+
stack: isSystemError ? err.stack : void 0,
|
|
2004
|
+
nodeID: ctx.nodeID,
|
|
2005
|
+
level: ctx.level,
|
|
2006
|
+
tracing: ctx.tracing
|
|
2007
|
+
};
|
|
2008
|
+
Object.keys(logData).forEach(
|
|
2009
|
+
(key) => logData[key] === void 0 && delete logData[key]
|
|
2010
|
+
);
|
|
2011
|
+
if (isSystemError) {
|
|
2012
|
+
this.logger.error("Unhandled Service Error", logData);
|
|
2013
|
+
} else {
|
|
2014
|
+
this.logger.warn("Service Error", logData);
|
|
2015
|
+
}
|
|
2016
|
+
if (err instanceof Errors.MoleculerError) {
|
|
2017
|
+
throw err;
|
|
2018
|
+
}
|
|
2019
|
+
throw new Errors.MoleculerError(
|
|
2020
|
+
err.message || "Internal server error",
|
|
2021
|
+
statusCode,
|
|
2022
|
+
errorType,
|
|
2023
|
+
{
|
|
2024
|
+
original: err.message,
|
|
2025
|
+
stack: isSystemError ? err.stack : void 0,
|
|
2026
|
+
ctx: ctx.id,
|
|
2027
|
+
service: this.name,
|
|
2028
|
+
action: action?.name
|
|
2029
|
+
}
|
|
2030
|
+
);
|
|
2031
|
+
}
|
|
1438
2032
|
};
|
|
1439
2033
|
var moleculer_default = configs;
|
|
1440
2034
|
|
|
@@ -2011,4 +2605,4 @@ var retryExtension = {
|
|
|
2011
2605
|
}
|
|
2012
2606
|
};
|
|
2013
2607
|
|
|
2014
|
-
export { AbstractDatasource, CHANNELS, ContextHelpersMiddleware, CreateHealthCheckMiddleware, DEFAULT_DATASOURCE_CACHE_TTL, DatasourceMixin, HEALTH_CHECK_DEFAULTS, INTEGRATION_CHANNELS, IntegrationCapability, IntegrationPlatform, IntegrationStatus, MemoizeMixin, MessageContentType, NAMESPACE, PERMISSIONS, PermissionsMiddleware, PrismaDatasource, PrismaPgDatasource, REDIS_URL, ROLE_PERMISSIONS, UserRole, createDatasourceMiddleware, createTenantExtension, defineIntegration, defineService, env_default as env, isDev, isProd, isTest, moleculer_default as moleculer, nodeEnv, omit, retryExtension, softDeleteExtension };
|
|
2608
|
+
export { AbstractDatasource, AuthenticationError, CHANNELS, ContextHelpersMiddleware, CreateHealthCheckMiddleware, DBConnectionError, DBConstraintError, DBError, DBNotFoundError, DBTimeoutError, DBTransactionError, DBValidationError, DEFAULT_DATASOURCE_CACHE_TTL, DatasourceMixin, HEALTH_CHECK_DEFAULTS, INTEGRATION_CHANNELS, IntegrationCapability, IntegrationPlatform, IntegrationStatus, MemoizeMixin, MessageContentType, NAMESPACE, PERMISSIONS, PermissionError, PermissionsMiddleware, PrismaDatasource, PrismaPgDatasource, REDIS_URL, ROLE_PERMISSIONS, TenantError, UserRole, createDatasourceMiddleware, createTenantExtension, defineIntegration, defineService, env_default as env, gatewayErrorHandler, getErrorLogLevel, isDev, isProd, isTest, moleculer_default as moleculer, nodeEnv, omit, prismaErrorHandler, retryExtension, sanitizeErrorData, softDeleteExtension };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hiliosai/sdk",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
"@hiliosai/typescript": "workspace:*",
|
|
30
30
|
"@pkg/dev-utils": "workspace:*",
|
|
31
31
|
"@prisma/adapter-pg": "7.2.0",
|
|
32
|
+
"@prisma/client": "7.2.0",
|
|
32
33
|
"bun-types": "latest",
|
|
33
34
|
"nats": "2.29.3"
|
|
34
35
|
},
|
|
35
36
|
"prettier": "@hiliosai/prettier"
|
|
36
|
-
}
|
|
37
|
+
}
|