logiscout 1.0.0 → 1.0.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.
@@ -6,12 +6,42 @@ const crypto_1 = require("crypto");
6
6
  const storage = new async_hooks_1.AsyncLocalStorage();
7
7
  class RequestContext {
8
8
  static run(fn, correlationId) {
9
- storage.run({
10
- correlationId: correlationId ?? (0, crypto_1.randomUUID)(),
11
- }, fn);
9
+ // Validate function parameter
10
+ if (typeof fn !== "function") {
11
+ throw new Error("RequestContext.run() requires a function as the first parameter. " +
12
+ "Usage: RequestContext.run(() => { ... }, correlationId?)");
13
+ }
14
+ // Validate correlationId if provided
15
+ if (correlationId !== undefined && typeof correlationId !== "string") {
16
+ throw new Error("RequestContext.run(): correlationId must be a string if provided. " +
17
+ `Received: ${typeof correlationId}`);
18
+ }
19
+ if (correlationId && correlationId.length > 100) {
20
+ throw new Error("RequestContext.run(): correlationId cannot exceed 100 characters.");
21
+ }
22
+ // Generate UUID if not provided
23
+ const id = correlationId ?? (0, crypto_1.randomUUID)();
24
+ try {
25
+ storage.run({
26
+ correlationId: id,
27
+ }, fn);
28
+ }
29
+ catch (error) {
30
+ throw new Error(`RequestContext.run(): Failed to run function in context. ${error instanceof Error ? error.message : "Unknown error"}`);
31
+ }
12
32
  }
13
33
  static getCorrelationId() {
14
- return storage.getStore()?.correlationId;
34
+ try {
35
+ const store = storage.getStore();
36
+ return store?.correlationId;
37
+ }
38
+ catch (error) {
39
+ // AsyncLocalStorage access failed - likely outside async context
40
+ console.warn("[RequestContext] Warning: Could not retrieve correlationId. " +
41
+ "This may occur outside of a RequestContext.run() block. " +
42
+ "Consider using createCorrelationMiddleware() for HTTP requests.");
43
+ return undefined;
44
+ }
15
45
  }
16
46
  }
17
47
  exports.RequestContext = RequestContext;
@@ -1 +1 @@
1
- {"version":3,"file":"RequestContext.d.ts","sourceRoot":"","sources":["../../../../src/core/context/RequestContext.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,cAAc;IACzB,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IASxD,MAAM,CAAC,gBAAgB,IAAI,MAAM,GAAG,SAAS;CAG9C"}
1
+ {"version":3,"file":"RequestContext.d.ts","sourceRoot":"","sources":["../../../../src/core/context/RequestContext.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,cAAc;IACzB,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IA0CxD,MAAM,CAAC,gBAAgB,IAAI,MAAM,GAAG,SAAS;CAc9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAGrD,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,WACvB,CAAC;AAGxB,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAGrD,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,WACvB,CAAC;AAGxB,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAA"}
@@ -8,13 +8,61 @@ const RequestContext_js_1 = require("../core/context/RequestContext.cjs");
8
8
  const crypto_1 = __importDefault(require("crypto"));
9
9
  const createCorrelationMiddleware = () => {
10
10
  return (req, res, next) => {
11
- const correlationId = crypto_1.default.randomUUID();
12
- RequestContext_js_1.RequestContext.run(() => {
13
- if (res?.setHeader) {
14
- res.setHeader("x-correlation-id", correlationId);
11
+ // Validate request object
12
+ if (!req || typeof req !== "object") {
13
+ console.error("[CorrelationMiddleware] Error: Invalid request object. " +
14
+ "Usage: createCorrelationMiddleware()(req, res, next)");
15
+ next?.(new Error("Invalid request object in correlation middleware"));
16
+ return;
17
+ }
18
+ // Validate response object
19
+ if (!res || typeof res !== "object") {
20
+ console.error("[CorrelationMiddleware] Error: Invalid response object. " +
21
+ "Usage: createCorrelationMiddleware()(req, res, next)");
22
+ next?.(new Error("Invalid response object in correlation middleware"));
23
+ return;
24
+ }
25
+ try {
26
+ // Check for existing correlation ID in headers (case-insensitive)
27
+ const headers = req.headers || {};
28
+ const existingId = headers["x-correlation-id"] ||
29
+ headers["X-Correlation-ID"] ||
30
+ req.get?.("x-correlation-id") ||
31
+ req.get?.("X-Correlation-ID");
32
+ const correlationId = (typeof existingId === "string" && existingId.trim()) || crypto_1.default.randomUUID();
33
+ // Validate correlation ID
34
+ if (!correlationId || typeof correlationId !== "string") {
35
+ console.error("[CorrelationMiddleware] Error: Failed to generate or retrieve correlation ID");
36
+ next?.(new Error("Correlation ID generation failed"));
37
+ return;
15
38
  }
16
- next();
17
- }, correlationId);
39
+ if (correlationId.length > 100) {
40
+ console.warn("[CorrelationMiddleware] Warning: Correlation ID exceeds 100 characters and will be truncated");
41
+ }
42
+ // Run the remaining middleware in the request context
43
+ RequestContext_js_1.RequestContext.run(() => {
44
+ // Safely set the correlation ID header
45
+ try {
46
+ if (res.setHeader) {
47
+ res.setHeader("x-correlation-id", correlationId);
48
+ }
49
+ }
50
+ catch (headerError) {
51
+ // setHeader might fail in some edge cases, don't block the request
52
+ console.warn("[CorrelationMiddleware] Warning: Could not set x-correlation-id header", headerError instanceof Error ? headerError.message : "Unknown error");
53
+ }
54
+ // Store correlation ID in response locals for downstream access
55
+ if (res.locals) {
56
+ res.locals.correlationId = correlationId;
57
+ }
58
+ next?.();
59
+ }, correlationId);
60
+ }
61
+ catch (error) {
62
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
63
+ console.error(`[CorrelationMiddleware] Error: Failed to initialize request context. ${errorMessage}`);
64
+ next?.(error instanceof Error ? error : new Error("Request context initialization failed"));
65
+ }
18
66
  };
19
67
  };
20
68
  exports.createCorrelationMiddleware = createCorrelationMiddleware;
@@ -1,2 +1,17 @@
1
- export declare const createCorrelationMiddleware: () => (req: any, res: any, next: () => void) => void;
1
+ interface RequestLike {
2
+ headers?: Record<string, string | string[] | undefined>;
3
+ get?: (header: string) => string | undefined;
4
+ }
5
+ interface ResponseLike {
6
+ setHeader?: (name: string, value: string) => void;
7
+ locals?: Record<string, unknown>;
8
+ }
9
+ interface NextFunction {
10
+ (err?: Error): void;
11
+ }
12
+ interface MiddlewareHandler {
13
+ (req: RequestLike, res: ResponseLike, next: NextFunction): void;
14
+ }
15
+ export declare const createCorrelationMiddleware: () => MiddlewareHandler;
16
+ export {};
2
17
  //# sourceMappingURL=correlationMiddleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"correlationMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middlewares/correlationMiddleware.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,2BAA2B,SAC9B,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI,SAU7C,CAAC"}
1
+ {"version":3,"file":"correlationMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middlewares/correlationMiddleware.ts"],"names":[],"mappings":"AAGA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,UAAU,YAAY;IACpB,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,UAAU,iBAAiB;IACzB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,2BAA2B,QAAO,iBAkF9C,CAAC"}
@@ -20,5 +20,9 @@ class Levels extends Logger_js_1.Logger {
20
20
  const entry = this.getLogEntry(message, LogLevels_js_1.LogLevels.DEBUG, meta);
21
21
  this.log(entry);
22
22
  }
23
+ critical(message, meta) {
24
+ const entry = this.getLogEntry(message, LogLevels_js_1.LogLevels.CRITICAL, meta);
25
+ this.log(entry);
26
+ }
23
27
  }
24
28
  exports.Levels = Levels;
@@ -5,5 +5,6 @@ export declare class Levels extends Logger implements LoggerInterface {
5
5
  warn(message: string, meta?: Record<string, unknown>): void;
6
6
  error(message: string, meta?: Record<string, unknown>): void;
7
7
  debug(message: string, meta?: Record<string, unknown>): void;
8
+ critical(message: string, meta?: Record<string, unknown>): void;
8
9
  }
9
10
  //# sourceMappingURL=Levels.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Levels.d.ts","sourceRoot":"","sources":["../../../../src/services/Levels/Levels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE1E,qBAAa,MAAO,SAAQ,MAAO,YAAW,eAAe;IAC3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAI7D"}
1
+ {"version":3,"file":"Levels.d.ts","sourceRoot":"","sources":["../../../../src/services/Levels/Levels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE1E,qBAAa,MAAO,SAAQ,MAAO,YAAW,eAAe;IAC3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAKhE"}
@@ -35,42 +35,84 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.Logger = void 0;
37
37
  const winston_1 = __importStar(require("winston"));
38
+ const LogLevels_js_1 = require("../../core/enum/LogLevels.cjs");
38
39
  const RequestContext_js_1 = require("../../core/context/RequestContext.cjs");
39
40
  class Logger {
40
41
  winstonLogger;
41
42
  componentName;
42
43
  constructor(componentName) {
44
+ // Validate component name
45
+ if (!componentName || typeof componentName !== "string") {
46
+ throw new Error("Logger: Component name must be a non-empty string. " +
47
+ "Usage: createLogger('ComponentName')");
48
+ }
49
+ if (componentName.length > 100) {
50
+ throw new Error("Logger: Component name cannot exceed 100 characters. " +
51
+ `Received: '${componentName.substring(0, 20)}...'`);
52
+ }
43
53
  this.componentName = componentName;
44
- this.winstonLogger = winston_1.default.createLogger({
45
- level: "debug",
46
- levels: {
47
- error: 0,
48
- warn: 1,
49
- info: 2,
50
- debug: 3,
51
- },
52
- format: winston_1.format.combine(winston_1.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston_1.format.printf(({ timestamp, level, message, component, correlationId, ...meta }) => {
53
- const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
54
- return `[${component}] [${timestamp}] [${level.toUpperCase()}] [${correlationId}] ${message}${metaString}`;
55
- })),
56
- transports: [
57
- new winston_1.transports.Console({
58
- format: winston_1.format.combine(winston_1.format.colorize({ all: true }), winston_1.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston_1.format.printf(({ timestamp, level, message, ...meta }) => {
59
- const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
60
- return `[${componentName}] [${timestamp}] [${level}] ${message}${metaString}`;
61
- })),
62
- }),
63
- ],
64
- });
54
+ try {
55
+ this.winstonLogger = winston_1.default.createLogger({
56
+ level: "debug",
57
+ levels: {
58
+ error: 0,
59
+ warn: 1,
60
+ info: 2,
61
+ debug: 3,
62
+ },
63
+ format: winston_1.format.combine(winston_1.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston_1.format.printf(({ timestamp, level, message, component, correlationId, ...meta }) => {
64
+ const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
65
+ return `[${component}] [${timestamp}] [${level.toUpperCase()}] [${correlationId}] ${message}${metaString}`;
66
+ })),
67
+ transports: [
68
+ new winston_1.transports.Console({
69
+ format: winston_1.format.combine(winston_1.format.colorize({ all: true }), winston_1.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), winston_1.format.printf(({ timestamp, level, message, ...meta }) => {
70
+ const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
71
+ return `[${componentName}] [${timestamp}] [${level}] ${message}${metaString}`;
72
+ })),
73
+ }),
74
+ ],
75
+ });
76
+ }
77
+ catch (error) {
78
+ throw new Error(`Logger: Failed to initialize Winston logger. ${error instanceof Error ? error.message : "Unknown error"}`);
79
+ }
65
80
  }
66
81
  log(entry) {
67
82
  try {
68
- const correlationId = RequestContext_js_1.RequestContext.getCorrelationId();
69
- const logMeta = {
70
- component: this.componentName,
71
- correlationId,
72
- ...entry.meta,
73
- };
83
+ // Validate log entry
84
+ if (!entry) {
85
+ console.error("[Logiscout] Error: Log entry is undefined or null");
86
+ return;
87
+ }
88
+ if (!entry.message || typeof entry.message !== "string") {
89
+ console.error("[Logiscout] Error: Log message must be a non-empty string. " +
90
+ `Usage: logiscout.${entry.level}('Your message', { meta })`);
91
+ return;
92
+ }
93
+ if (entry.message.length > 10000) {
94
+ console.error("[Logiscout] Error: Log message exceeds 10000 characters. " +
95
+ "Message truncated for logging.");
96
+ entry.message = entry.message.substring(0, 10000) + "... [truncated]";
97
+ }
98
+ const correlationId = RequestContext_js_1.RequestContext.getCorrelationId() ?? "no-correlation-id";
99
+ // Safely handle metadata - prevent circular reference issues
100
+ let logMeta;
101
+ try {
102
+ logMeta = {
103
+ component: this.componentName,
104
+ correlationId,
105
+ ...entry.meta,
106
+ };
107
+ }
108
+ catch (metaError) {
109
+ console.error("[Logiscout] Error: Failed to process metadata. Logging without metadata.", metaError instanceof Error ? metaError.message : "Unknown error");
110
+ logMeta = {
111
+ component: this.componentName,
112
+ correlationId,
113
+ _metaError: "Failed to process metadata",
114
+ };
115
+ }
74
116
  this.winstonLogger.log(entry.level, entry.message, logMeta);
75
117
  // Send structured log to API (no formatting)
76
118
  // LogApi({
@@ -81,14 +123,27 @@ class Logger {
81
123
  // },
82
124
  // });
83
125
  }
84
- catch {
85
- // never throw from logger
126
+ catch (error) {
127
+ // Silent failure - logger should never crash the application
128
+ // Log to console as last resort
129
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
130
+ console.error(`[Logiscout] Critical: Logger failed to write log: ${errorMessage}`);
86
131
  }
87
132
  }
88
133
  getWinstonLogger() {
89
134
  return this.winstonLogger;
90
135
  }
91
136
  getLogEntry(message, logLevel, meta) {
137
+ // Validate message
138
+ if (!message || typeof message !== "string") {
139
+ throw new Error(`Logger: Message must be a non-empty string. ` +
140
+ `Received: ${typeof message === "undefined" ? "undefined" : typeof message}`);
141
+ }
142
+ // Validate log level
143
+ if (!Object.values(LogLevels_js_1.LogLevels).includes(logLevel)) {
144
+ throw new Error(`Logger: Invalid log level '${logLevel}'. ` +
145
+ `Valid levels: ${Object.values(LogLevels_js_1.LogLevels).join(", ")}`);
146
+ }
92
147
  const entry = {
93
148
  message,
94
149
  meta,
@@ -1 +1 @@
1
- {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../../../../src/services/Logger/Logger.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,MAAM,IAAI,aAAa,EAAsB,MAAM,SAAS,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,8BAAsB,MAAM;IAC1B,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,OAAO,CAAC,aAAa,CAAS;gBAGlB,aAAa,EAAE,MAAM;IA4CjC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAyBpC,gBAAgB,IAAI,aAAa;IAIjC,SAAS,CAAC,WAAW,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,EACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,QAAQ;CASZ"}
1
+ {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../../../../src/services/Logger/Logger.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,MAAM,IAAI,aAAa,EAAsB,MAAM,SAAS,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,8BAAsB,MAAM;IAC1B,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,OAAO,CAAC,aAAa,CAAS;gBAElB,aAAa,EAAE,MAAM;IAkEjC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAgEpC,gBAAgB,IAAI,aAAa;IAIjC,SAAS,CAAC,WAAW,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,EACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,QAAQ;CAyBZ"}
@@ -1 +1 @@
1
- {"version":3,"file":"RequestContext.d.ts","sourceRoot":"","sources":["../../../../src/core/context/RequestContext.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,cAAc;IACzB,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IASxD,MAAM,CAAC,gBAAgB,IAAI,MAAM,GAAG,SAAS;CAG9C"}
1
+ {"version":3,"file":"RequestContext.d.ts","sourceRoot":"","sources":["../../../../src/core/context/RequestContext.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IACjC,aAAa,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,cAAc;IACzB,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IA0CxD,MAAM,CAAC,gBAAgB,IAAI,MAAM,GAAG,SAAS;CAc9C"}
@@ -3,11 +3,41 @@ import { randomUUID } from "crypto";
3
3
  const storage = new AsyncLocalStorage();
4
4
  export class RequestContext {
5
5
  static run(fn, correlationId) {
6
- storage.run({
7
- correlationId: correlationId ?? randomUUID(),
8
- }, fn);
6
+ // Validate function parameter
7
+ if (typeof fn !== "function") {
8
+ throw new Error("RequestContext.run() requires a function as the first parameter. " +
9
+ "Usage: RequestContext.run(() => { ... }, correlationId?)");
10
+ }
11
+ // Validate correlationId if provided
12
+ if (correlationId !== undefined && typeof correlationId !== "string") {
13
+ throw new Error("RequestContext.run(): correlationId must be a string if provided. " +
14
+ `Received: ${typeof correlationId}`);
15
+ }
16
+ if (correlationId && correlationId.length > 100) {
17
+ throw new Error("RequestContext.run(): correlationId cannot exceed 100 characters.");
18
+ }
19
+ // Generate UUID if not provided
20
+ const id = correlationId ?? randomUUID();
21
+ try {
22
+ storage.run({
23
+ correlationId: id,
24
+ }, fn);
25
+ }
26
+ catch (error) {
27
+ throw new Error(`RequestContext.run(): Failed to run function in context. ${error instanceof Error ? error.message : "Unknown error"}`);
28
+ }
9
29
  }
10
30
  static getCorrelationId() {
11
- return storage.getStore()?.correlationId;
31
+ try {
32
+ const store = storage.getStore();
33
+ return store?.correlationId;
34
+ }
35
+ catch (error) {
36
+ // AsyncLocalStorage access failed - likely outside async context
37
+ console.warn("[RequestContext] Warning: Could not retrieve correlationId. " +
38
+ "This may occur outside of a RequestContext.run() block. " +
39
+ "Consider using createCorrelationMiddleware() for HTTP requests.");
40
+ return undefined;
41
+ }
12
42
  }
13
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAGrD,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,WACvB,CAAC;AAGxB,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AACrD,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,6BAA6B,CAAC;AAGrD,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,WACvB,CAAC;AAGxB,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAA"}
@@ -1,2 +1,17 @@
1
- export declare const createCorrelationMiddleware: () => (req: any, res: any, next: () => void) => void;
1
+ interface RequestLike {
2
+ headers?: Record<string, string | string[] | undefined>;
3
+ get?: (header: string) => string | undefined;
4
+ }
5
+ interface ResponseLike {
6
+ setHeader?: (name: string, value: string) => void;
7
+ locals?: Record<string, unknown>;
8
+ }
9
+ interface NextFunction {
10
+ (err?: Error): void;
11
+ }
12
+ interface MiddlewareHandler {
13
+ (req: RequestLike, res: ResponseLike, next: NextFunction): void;
14
+ }
15
+ export declare const createCorrelationMiddleware: () => MiddlewareHandler;
16
+ export {};
2
17
  //# sourceMappingURL=correlationMiddleware.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"correlationMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middlewares/correlationMiddleware.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,2BAA2B,SAC9B,KAAK,GAAG,EAAE,KAAK,GAAG,EAAE,MAAM,MAAM,IAAI,SAU7C,CAAC"}
1
+ {"version":3,"file":"correlationMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middlewares/correlationMiddleware.ts"],"names":[],"mappings":"AAGA,UAAU,WAAW;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACxD,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC9C;AAED,UAAU,YAAY;IACpB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,UAAU,YAAY;IACpB,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAED,UAAU,iBAAiB;IACzB,CAAC,GAAG,EAAE,WAAW,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;CACjE;AAED,eAAO,MAAM,2BAA2B,QAAO,iBAkF9C,CAAC"}
@@ -2,12 +2,60 @@ import { RequestContext } from "../core/context/RequestContext.js";
2
2
  import crypto from "crypto";
3
3
  export const createCorrelationMiddleware = () => {
4
4
  return (req, res, next) => {
5
- const correlationId = crypto.randomUUID();
6
- RequestContext.run(() => {
7
- if (res?.setHeader) {
8
- res.setHeader("x-correlation-id", correlationId);
5
+ // Validate request object
6
+ if (!req || typeof req !== "object") {
7
+ console.error("[CorrelationMiddleware] Error: Invalid request object. " +
8
+ "Usage: createCorrelationMiddleware()(req, res, next)");
9
+ next?.(new Error("Invalid request object in correlation middleware"));
10
+ return;
11
+ }
12
+ // Validate response object
13
+ if (!res || typeof res !== "object") {
14
+ console.error("[CorrelationMiddleware] Error: Invalid response object. " +
15
+ "Usage: createCorrelationMiddleware()(req, res, next)");
16
+ next?.(new Error("Invalid response object in correlation middleware"));
17
+ return;
18
+ }
19
+ try {
20
+ // Check for existing correlation ID in headers (case-insensitive)
21
+ const headers = req.headers || {};
22
+ const existingId = headers["x-correlation-id"] ||
23
+ headers["X-Correlation-ID"] ||
24
+ req.get?.("x-correlation-id") ||
25
+ req.get?.("X-Correlation-ID");
26
+ const correlationId = (typeof existingId === "string" && existingId.trim()) || crypto.randomUUID();
27
+ // Validate correlation ID
28
+ if (!correlationId || typeof correlationId !== "string") {
29
+ console.error("[CorrelationMiddleware] Error: Failed to generate or retrieve correlation ID");
30
+ next?.(new Error("Correlation ID generation failed"));
31
+ return;
9
32
  }
10
- next();
11
- }, correlationId);
33
+ if (correlationId.length > 100) {
34
+ console.warn("[CorrelationMiddleware] Warning: Correlation ID exceeds 100 characters and will be truncated");
35
+ }
36
+ // Run the remaining middleware in the request context
37
+ RequestContext.run(() => {
38
+ // Safely set the correlation ID header
39
+ try {
40
+ if (res.setHeader) {
41
+ res.setHeader("x-correlation-id", correlationId);
42
+ }
43
+ }
44
+ catch (headerError) {
45
+ // setHeader might fail in some edge cases, don't block the request
46
+ console.warn("[CorrelationMiddleware] Warning: Could not set x-correlation-id header", headerError instanceof Error ? headerError.message : "Unknown error");
47
+ }
48
+ // Store correlation ID in response locals for downstream access
49
+ if (res.locals) {
50
+ res.locals.correlationId = correlationId;
51
+ }
52
+ next?.();
53
+ }, correlationId);
54
+ }
55
+ catch (error) {
56
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
57
+ console.error(`[CorrelationMiddleware] Error: Failed to initialize request context. ${errorMessage}`);
58
+ next?.(error instanceof Error ? error : new Error("Request context initialization failed"));
59
+ }
12
60
  };
13
61
  };
@@ -5,5 +5,6 @@ export declare class Levels extends Logger implements LoggerInterface {
5
5
  warn(message: string, meta?: Record<string, unknown>): void;
6
6
  error(message: string, meta?: Record<string, unknown>): void;
7
7
  debug(message: string, meta?: Record<string, unknown>): void;
8
+ critical(message: string, meta?: Record<string, unknown>): void;
8
9
  }
9
10
  //# sourceMappingURL=Levels.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Levels.d.ts","sourceRoot":"","sources":["../../../../src/services/Levels/Levels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE1E,qBAAa,MAAO,SAAQ,MAAO,YAAW,eAAe;IAC3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAI7D"}
1
+ {"version":3,"file":"Levels.d.ts","sourceRoot":"","sources":["../../../../src/services/Levels/Levels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAE1E,qBAAa,MAAO,SAAQ,MAAO,YAAW,eAAe;IAC3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAK5D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAKhE"}
@@ -17,4 +17,8 @@ export class Levels extends Logger {
17
17
  const entry = this.getLogEntry(message, LogLevels.DEBUG, meta);
18
18
  this.log(entry);
19
19
  }
20
+ critical(message, meta) {
21
+ const entry = this.getLogEntry(message, LogLevels.CRITICAL, meta);
22
+ this.log(entry);
23
+ }
20
24
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../../../../src/services/Logger/Logger.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,MAAM,IAAI,aAAa,EAAsB,MAAM,SAAS,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,8BAAsB,MAAM;IAC1B,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,OAAO,CAAC,aAAa,CAAS;gBAGlB,aAAa,EAAE,MAAM;IA4CjC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAyBpC,gBAAgB,IAAI,aAAa;IAIjC,SAAS,CAAC,WAAW,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,EACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,QAAQ;CASZ"}
1
+ {"version":3,"file":"Logger.d.ts","sourceRoot":"","sources":["../../../../src/services/Logger/Logger.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,MAAM,IAAI,aAAa,EAAsB,MAAM,SAAS,CAAC;AAE/E,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAGxD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAGzD,8BAAsB,MAAM;IAC1B,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,OAAO,CAAC,aAAa,CAAS;gBAElB,aAAa,EAAE,MAAM;IAkEjC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAgEpC,gBAAgB,IAAI,aAAa;IAIjC,SAAS,CAAC,WAAW,CACnB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,SAAS,EACnB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,QAAQ;CAyBZ"}
@@ -1,40 +1,82 @@
1
1
  import winston, { format, transports } from "winston";
2
+ import { LogLevels } from "../../core/enum/LogLevels.js";
2
3
  import { RequestContext } from "../../core/context/RequestContext.js";
3
4
  export class Logger {
4
5
  winstonLogger;
5
6
  componentName;
6
7
  constructor(componentName) {
8
+ // Validate component name
9
+ if (!componentName || typeof componentName !== "string") {
10
+ throw new Error("Logger: Component name must be a non-empty string. " +
11
+ "Usage: createLogger('ComponentName')");
12
+ }
13
+ if (componentName.length > 100) {
14
+ throw new Error("Logger: Component name cannot exceed 100 characters. " +
15
+ `Received: '${componentName.substring(0, 20)}...'`);
16
+ }
7
17
  this.componentName = componentName;
8
- this.winstonLogger = winston.createLogger({
9
- level: "debug",
10
- levels: {
11
- error: 0,
12
- warn: 1,
13
- info: 2,
14
- debug: 3,
15
- },
16
- format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.printf(({ timestamp, level, message, component, correlationId, ...meta }) => {
17
- const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
18
- return `[${component}] [${timestamp}] [${level.toUpperCase()}] [${correlationId}] ${message}${metaString}`;
19
- })),
20
- transports: [
21
- new transports.Console({
22
- format: format.combine(format.colorize({ all: true }), format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.printf(({ timestamp, level, message, ...meta }) => {
23
- const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
24
- return `[${componentName}] [${timestamp}] [${level}] ${message}${metaString}`;
25
- })),
26
- }),
27
- ],
28
- });
18
+ try {
19
+ this.winstonLogger = winston.createLogger({
20
+ level: "debug",
21
+ levels: {
22
+ error: 0,
23
+ warn: 1,
24
+ info: 2,
25
+ debug: 3,
26
+ },
27
+ format: format.combine(format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.printf(({ timestamp, level, message, component, correlationId, ...meta }) => {
28
+ const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
29
+ return `[${component}] [${timestamp}] [${level.toUpperCase()}] [${correlationId}] ${message}${metaString}`;
30
+ })),
31
+ transports: [
32
+ new transports.Console({
33
+ format: format.combine(format.colorize({ all: true }), format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }), format.printf(({ timestamp, level, message, ...meta }) => {
34
+ const metaString = Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
35
+ return `[${componentName}] [${timestamp}] [${level}] ${message}${metaString}`;
36
+ })),
37
+ }),
38
+ ],
39
+ });
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Logger: Failed to initialize Winston logger. ${error instanceof Error ? error.message : "Unknown error"}`);
43
+ }
29
44
  }
30
45
  log(entry) {
31
46
  try {
32
- const correlationId = RequestContext.getCorrelationId();
33
- const logMeta = {
34
- component: this.componentName,
35
- correlationId,
36
- ...entry.meta,
37
- };
47
+ // Validate log entry
48
+ if (!entry) {
49
+ console.error("[Logiscout] Error: Log entry is undefined or null");
50
+ return;
51
+ }
52
+ if (!entry.message || typeof entry.message !== "string") {
53
+ console.error("[Logiscout] Error: Log message must be a non-empty string. " +
54
+ `Usage: logiscout.${entry.level}('Your message', { meta })`);
55
+ return;
56
+ }
57
+ if (entry.message.length > 10000) {
58
+ console.error("[Logiscout] Error: Log message exceeds 10000 characters. " +
59
+ "Message truncated for logging.");
60
+ entry.message = entry.message.substring(0, 10000) + "... [truncated]";
61
+ }
62
+ const correlationId = RequestContext.getCorrelationId() ?? "no-correlation-id";
63
+ // Safely handle metadata - prevent circular reference issues
64
+ let logMeta;
65
+ try {
66
+ logMeta = {
67
+ component: this.componentName,
68
+ correlationId,
69
+ ...entry.meta,
70
+ };
71
+ }
72
+ catch (metaError) {
73
+ console.error("[Logiscout] Error: Failed to process metadata. Logging without metadata.", metaError instanceof Error ? metaError.message : "Unknown error");
74
+ logMeta = {
75
+ component: this.componentName,
76
+ correlationId,
77
+ _metaError: "Failed to process metadata",
78
+ };
79
+ }
38
80
  this.winstonLogger.log(entry.level, entry.message, logMeta);
39
81
  // Send structured log to API (no formatting)
40
82
  // LogApi({
@@ -45,14 +87,27 @@ export class Logger {
45
87
  // },
46
88
  // });
47
89
  }
48
- catch {
49
- // never throw from logger
90
+ catch (error) {
91
+ // Silent failure - logger should never crash the application
92
+ // Log to console as last resort
93
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
94
+ console.error(`[Logiscout] Critical: Logger failed to write log: ${errorMessage}`);
50
95
  }
51
96
  }
52
97
  getWinstonLogger() {
53
98
  return this.winstonLogger;
54
99
  }
55
100
  getLogEntry(message, logLevel, meta) {
101
+ // Validate message
102
+ if (!message || typeof message !== "string") {
103
+ throw new Error(`Logger: Message must be a non-empty string. ` +
104
+ `Received: ${typeof message === "undefined" ? "undefined" : typeof message}`);
105
+ }
106
+ // Validate log level
107
+ if (!Object.values(LogLevels).includes(logLevel)) {
108
+ throw new Error(`Logger: Invalid log level '${logLevel}'. ` +
109
+ `Valid levels: ${Object.values(LogLevels).join(", ")}`);
110
+ }
56
111
  const entry = {
57
112
  message,
58
113
  meta,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "logiscout",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Logiscout — a structured logger library",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.cjs",
@@ -26,22 +26,18 @@
26
26
  "keywords": [
27
27
  "logger",
28
28
  "logging",
29
- "correlation-id",
30
- "async-context",
31
29
  "structured-logging",
32
- "nodejs",
33
- "express",
34
- "winston"
30
+ "nodejs"
35
31
  ],
36
- "author": "YOUR_NAME",
32
+ "author": "Logiscout",
37
33
  "license": "MIT",
38
34
  "repository": {
39
35
  "type": "git",
40
- "url": "https://github.com/YOUR_GITHUB_USERNAME/logicout"
36
+ "url": "https://github.com/saadakmal460/Logiscout.git"
41
37
  },
42
- "homepage": "https://github.com/YOUR_GITHUB_USERNAME/logicout#readme",
38
+ "homepage": "https://github.com/saadakmal460/Logiscout#readme",
43
39
  "bugs": {
44
- "url": "https://github.com/YOUR_GITHUB_USERNAME/logicout/issues"
40
+ "url": "https://github.com/saadakmal460/Logiscout/issues"
45
41
  },
46
42
  "engines": {
47
43
  "node": ">=18"