@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 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
- export { AbstractDatasource, type ActionHandler, type ActionWithPermissions, type AppContext, type AppMeta, type AuditTrailExtension, type BaseDatasource, type BaseSpec, CHANNELS, type CarouselItem, type ChannelSendOptions, ContextHelpersMiddleware, CreateHealthCheckMiddleware, DEFAULT_DATASOURCE_CACHE_TTL, type DatasourceConstructorRegistry, type DatasourceContext, type DatasourceInstanceRegistry, type DatasourceInstanceTypes, DatasourceMixin, type Env, 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, 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, type TenantExtension, type User, UserRole, type WebhookEvent, createDatasourceMiddleware, createTenantExtension, defineIntegration, defineService, isDev, isProd, isTest, configs as moleculer, nodeEnv, omit, retryExtension, softDeleteExtension };
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.30",
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
+ }