@scpxl/nodejs-framework 1.0.24 → 1.0.25

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.
Files changed (101) hide show
  1. package/README.md +65 -17
  2. package/dist/api-requester/api-requester.d.ts.map +1 -1
  3. package/dist/api-requester/api-requester.js +2 -1
  4. package/dist/api-requester/api-requester.js.map +2 -2
  5. package/dist/application/base-application.d.ts.map +1 -1
  6. package/dist/application/base-application.js +4 -3
  7. package/dist/application/base-application.js.map +2 -2
  8. package/dist/cache/manager.d.ts.map +1 -1
  9. package/dist/cache/manager.js +2 -1
  10. package/dist/cache/manager.js.map +2 -2
  11. package/dist/cli/index.js +7019 -83
  12. package/dist/cli/index.js.map +4 -4
  13. package/dist/cluster/cluster-manager.d.ts +3 -0
  14. package/dist/cluster/cluster-manager.d.ts.map +1 -1
  15. package/dist/cluster/cluster-manager.js +45 -8
  16. package/dist/cluster/cluster-manager.js.map +2 -2
  17. package/dist/database/dynamic-entity-form-decorators.d.ts +2 -2
  18. package/dist/database/dynamic-entity-form-decorators.d.ts.map +1 -1
  19. package/dist/database/dynamic-entity-form-decorators.js.map +1 -1
  20. package/dist/database/dynamic-entity.d.ts +7 -4
  21. package/dist/database/dynamic-entity.d.ts.map +1 -1
  22. package/dist/database/dynamic-entity.js +11 -1
  23. package/dist/database/dynamic-entity.js.map +2 -2
  24. package/dist/database/manager.d.ts.map +1 -1
  25. package/dist/database/manager.js +3 -2
  26. package/dist/database/manager.js.map +2 -2
  27. package/dist/error/error-reporter.d.ts +20 -7
  28. package/dist/error/error-reporter.d.ts.map +1 -1
  29. package/dist/error/error-reporter.js +32 -29
  30. package/dist/error/error-reporter.js.map +2 -2
  31. package/dist/error/index.d.ts +1 -1
  32. package/dist/error/index.d.ts.map +1 -1
  33. package/dist/error/index.js +3 -2
  34. package/dist/error/index.js.map +2 -2
  35. package/dist/event/controller/base.d.ts.map +1 -1
  36. package/dist/event/controller/base.js +2 -1
  37. package/dist/event/controller/base.js.map +2 -2
  38. package/dist/event/manager.d.ts.map +1 -1
  39. package/dist/event/manager.js +5 -4
  40. package/dist/event/manager.js.map +2 -2
  41. package/dist/lifecycle/exit.d.ts.map +1 -1
  42. package/dist/lifecycle/exit.js.map +2 -2
  43. package/dist/logger/logger.d.ts.map +1 -1
  44. package/dist/logger/logger.js +3 -2
  45. package/dist/logger/logger.js.map +2 -2
  46. package/dist/performance/performance-monitor.d.ts.map +1 -1
  47. package/dist/performance/performance-monitor.js +10 -3
  48. package/dist/performance/performance-monitor.js.map +2 -2
  49. package/dist/queue/processor/base.d.ts.map +1 -1
  50. package/dist/queue/processor/base.js +2 -1
  51. package/dist/queue/processor/base.js.map +2 -2
  52. package/dist/redis/manager.d.ts.map +1 -1
  53. package/dist/redis/manager.js +3 -2
  54. package/dist/redis/manager.js.map +2 -2
  55. package/dist/schemas/common.d.ts +197 -0
  56. package/dist/schemas/common.d.ts.map +1 -0
  57. package/dist/schemas/common.js +108 -0
  58. package/dist/schemas/common.js.map +7 -0
  59. package/dist/schemas/index.d.ts +6 -0
  60. package/dist/schemas/index.d.ts.map +1 -0
  61. package/dist/schemas/index.js +2 -0
  62. package/dist/schemas/index.js.map +7 -0
  63. package/dist/util/loader.d.ts.map +1 -1
  64. package/dist/util/loader.js.map +2 -2
  65. package/dist/webserver/controller/base.d.ts.map +1 -1
  66. package/dist/webserver/controller/base.js +4 -4
  67. package/dist/webserver/controller/base.js.map +2 -2
  68. package/dist/webserver/controller/entity.js.map +2 -2
  69. package/dist/webserver/controller/health.d.ts +0 -2
  70. package/dist/webserver/controller/health.d.ts.map +1 -1
  71. package/dist/webserver/controller/health.js +0 -14
  72. package/dist/webserver/controller/health.js.map +2 -2
  73. package/dist/webserver/webserver.d.ts.map +1 -1
  74. package/dist/webserver/webserver.interface.d.ts +2 -2
  75. package/dist/webserver/webserver.interface.d.ts.map +1 -1
  76. package/dist/webserver/webserver.interface.js.map +1 -1
  77. package/dist/webserver/webserver.js +2 -2
  78. package/dist/webserver/webserver.js.map +2 -2
  79. package/dist/websocket/index.d.ts +2 -0
  80. package/dist/websocket/index.d.ts.map +1 -1
  81. package/dist/websocket/index.js +2 -0
  82. package/dist/websocket/index.js.map +2 -2
  83. package/dist/websocket/websocket-auth.d.ts +17 -0
  84. package/dist/websocket/websocket-auth.d.ts.map +1 -0
  85. package/dist/websocket/websocket-auth.js +46 -0
  86. package/dist/websocket/websocket-auth.js.map +7 -0
  87. package/dist/websocket/websocket-client-manager.d.ts.map +1 -1
  88. package/dist/websocket/websocket-client-manager.js +6 -5
  89. package/dist/websocket/websocket-client-manager.js.map +2 -2
  90. package/dist/websocket/websocket-client.d.ts +29 -0
  91. package/dist/websocket/websocket-client.d.ts.map +1 -1
  92. package/dist/websocket/websocket-client.js +97 -3
  93. package/dist/websocket/websocket-client.js.map +2 -2
  94. package/dist/websocket/websocket-server.d.ts +10 -0
  95. package/dist/websocket/websocket-server.d.ts.map +1 -1
  96. package/dist/websocket/websocket-server.js +40 -52
  97. package/dist/websocket/websocket-server.js.map +2 -2
  98. package/dist/websocket/websocket.interface.d.ts +13 -0
  99. package/dist/websocket/websocket.interface.d.ts.map +1 -1
  100. package/dist/websocket/websocket.interface.js.map +2 -2
  101. package/package.json +9 -12
package/README.md CHANGED
@@ -110,6 +110,37 @@ await app.start();
110
110
  console.log(`Server running at http://localhost:3000`);
111
111
  ```
112
112
 
113
+ ### Type-Safe Routes with Zod
114
+
115
+ ```typescript
116
+ import { WebApplication } from '@scpxl/nodejs-framework';
117
+ import { defineRoute } from '@scpxl/nodejs-framework/webserver';
118
+ import { z } from 'zod';
119
+ import { PaginationQuerySchema, NumericIdSchema } from '@scpxl/nodejs-framework/schemas';
120
+
121
+ const app = new WebApplication({
122
+ /* config */
123
+ });
124
+
125
+ // Define a typed route with automatic validation
126
+ const getUserRoute = defineRoute({
127
+ method: 'GET',
128
+ url: '/users/:id',
129
+ schema: {
130
+ params: z.object({ id: NumericIdSchema }),
131
+ querystring: PaginationQuerySchema,
132
+ },
133
+ handler: async (request, reply) => {
134
+ // TypeScript knows request.params.id is a number
135
+ // and request.query has page/limit with defaults
136
+ const user = await db.findUser(request.params.id);
137
+ return { data: user };
138
+ },
139
+ });
140
+
141
+ app.webserver.route(getUserRoute);
142
+ ```
143
+
113
144
  ### With Database & Queue
114
145
 
115
146
  ```typescript
@@ -325,6 +356,27 @@ The framework is built around three main application types:
325
356
  | **API Requester** | HTTP client wrapper (migrated to native `fetch`) | `@scpxl/nodejs-framework/api-requester` |
326
357
  | **Command** | CLI command framework | `@scpxl/nodejs-framework/command` |
327
358
  | **Services** | Additional service integrations (AWS S3, etc.) | `@scpxl/nodejs-framework/services` |
359
+ | **Schemas** | Reusable Zod validation schemas | `@scpxl/nodejs-framework/schemas` |
360
+
361
+ ### Guides
362
+
363
+ Comprehensive guides for common tasks and features:
364
+
365
+ - **[Getting Started](./docs/getting-started.md)** - Installation and first steps
366
+ - **[Configuration](./docs/guides/configuration.md)** - Environment variables and config options
367
+ - **[WebSocket Guide](./docs/guides/websocket.md)** - Real-time communication setup
368
+ - **[Authentication Guide](./docs/guides/authentication.md)** - JWT auth implementation
369
+ - **[Typed Routes](./docs/guides/typed-routes.md)** - Type-safe routing with Zod validation
370
+ - **[Error Handling](./docs/guides/error-handling.md)** - Custom errors and error handling
371
+ - **[Commands](./docs/guides/commands.md)** - Building CLI commands
372
+ - **[Testing](./docs/guides/testing.md)** - Testing strategies and utilities
373
+ - **[Performance Monitoring](./docs/guides/performance-monitoring.md)** - Metrics and observability
374
+ - **[Simple Load Testing](./docs/guides/simple-load-test.md)** - Built-in load testing tool
375
+ - **[Hot Module Reload (HMR)](./docs/guides/hmr.md)** - Development workflow
376
+ - **[Deployment](./docs/guides/deployment.md)** - Production deployment guide
377
+ - **[Scaling](./docs/guides/scaling.md)** - Horizontal scaling strategies
378
+ - **[Logging](./docs/guides/logging.md)** - Structured logging best practices
379
+ - **[Environment Variables](./docs/guides/env.md)** - Managing environment configuration
328
380
 
329
381
  ### Key Patterns
330
382
 
@@ -412,15 +464,15 @@ A full-stack example with:
412
464
  **Run the example:**
413
465
 
414
466
  ```bash
415
- # Install dependencies for examples (one-time setup)
416
- npm run example:install
467
+ # Install dependencies (one-time setup)
468
+ cd examples/hello-world/backend && npm install
469
+ cd ../frontend && npm install
417
470
 
418
- # Run backend + frontend together with hot-reload
419
- npm run example:hello-world
471
+ # Run backend
472
+ npm run example --example=hello-world/backend
420
473
 
421
- # Or run individually
422
- npm run example:hello-world:backend
423
- npm run example:hello-world:frontend
474
+ # Or run frontend (in another terminal)
475
+ npm run example --example=hello-world/frontend
424
476
  ```
425
477
 
426
478
  Then open http://localhost:5173 to see the app.
@@ -430,17 +482,13 @@ Then open http://localhost:5173 to see the app.
430
482
  Demonstrates the command framework with examples:
431
483
 
432
484
  ```bash
433
- # Install dependencies
434
- npm run example:commands:install
435
-
436
- # Run hello command
437
- npm run example:commands:hello
438
-
439
- # Run database seed command
440
- npm run example:commands:seed
485
+ # Install dependencies (one-time setup)
486
+ cd examples/commands && npm install
441
487
 
442
- # Run queue processing command
443
- npm run example:commands:queue
488
+ # Run commands from repository root
489
+ npm run example --example=commands -- hello
490
+ npm run example --example=commands -- database-seed
491
+ npm run example --example=commands -- queue-process
444
492
  ```
445
493
 
446
494
  See [examples/README.md](examples/README.md) for more details.
@@ -1 +1 @@
1
- {"version":3,"file":"api-requester.d.ts","sourceRoot":"","sources":["../../src/api-requester/api-requester.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AA0BD,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAKpD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAInF,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAIhG,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAI/F,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;YAIrF,OAAO;IAsCrB,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;YAUT,aAAa;YAkBb,eAAe;IAoC7B,OAAO,CAAC,WAAW;CAapB"}
1
+ {"version":3,"file":"api-requester.d.ts","sourceRoot":"","sources":["../../src/api-requester/api-requester.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,WAAW,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AA0BD,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAKpD,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAInF,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAIhG,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAI/F,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;YAIrF,OAAO;IAsCrB,OAAO,CAAC,YAAY;IA0BpB,OAAO,CAAC,WAAW;IA4BnB,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;YAUT,aAAa;YAkBb,eAAe;IAoC7B,OAAO,CAAC,WAAW;CAapB"}
@@ -1,5 +1,6 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { safeSerializeError } from "../error/error-reporter.js";
3
4
  class ApiRequesterHttpError extends Error {
4
5
  static {
5
6
  __name(this, "ApiRequesterHttpError");
@@ -166,7 +167,7 @@ class ApiRequester {
166
167
  try {
167
168
  return JSON.parse(body);
168
169
  } catch (error) {
169
- const reason = error instanceof Error ? error.message : String(error);
170
+ const reason = error instanceof Error ? error.message : safeSerializeError(error);
170
171
  throw new Error(`Failed to parse JSON response: ${reason}`);
171
172
  }
172
173
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/api-requester/api-requester.ts"],
4
- "sourcesContent": ["export interface ApiRequestConfig extends Omit<RequestInit, 'method' | 'body' | 'headers'> {\n headers?: Record<string, string | undefined>;\n params?: Record<string, string | number | boolean | null | undefined>;\n responseType?: 'json' | 'text';\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nclass ApiRequesterHttpError extends Error {\n public readonly status: number;\n public readonly statusText: string;\n public readonly data: unknown;\n public readonly headers: Record<string, string>;\n\n constructor(\n message: string,\n options: {\n status: number;\n statusText: string;\n data: unknown;\n headers: Record<string, string>;\n },\n ) {\n super(message);\n this.name = 'ApiRequesterHttpError';\n this.status = options.status;\n this.statusText = options.statusText;\n this.data = options.data;\n this.headers = options.headers;\n }\n}\n\nexport default class ApiRequester {\n private readonly baseURL: string;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(baseURL: string, headers: Record<string, string> = {}) {\n this.baseURL = baseURL;\n this.defaultHeaders = { ...headers };\n }\n\n public async get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('GET', url, undefined, config);\n }\n\n public async post<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('POST', url, data, config);\n }\n\n public async put<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('PUT', url, data, config);\n }\n\n public async delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('DELETE', url, undefined, config);\n }\n\n private async request<T>(\n method: string,\n url: string,\n data?: unknown,\n config?: ApiRequestConfig,\n ): Promise<ApiResponse<T> | undefined> {\n try {\n const { headers: configHeaders, params, responseType = 'json', ...init } = config ?? {};\n const finalUrl = this.buildUrl(url, params);\n const headers = this.mergeHeaders(configHeaders, data);\n const body = this.prepareBody(method, data);\n\n // fetch is a global in Node.js 18+\n // eslint-disable-next-line no-undef\n const response = await fetch(finalUrl, {\n ...init,\n method,\n headers,\n body,\n });\n\n if (!response.ok) {\n throw await this.createHttpError(response);\n }\n\n const parsed = await this.parseResponse<T>(response, responseType);\n\n return {\n data: parsed,\n status: response.status,\n statusText: response.statusText,\n headers: this.headersToRecord(response.headers),\n };\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private mergeHeaders(headers?: Record<string, string | undefined>, body?: unknown): Headers {\n const merged = new Headers();\n\n for (const [key, value] of Object.entries(this.defaultHeaders)) {\n if (value !== undefined) {\n merged.set(key, value);\n }\n }\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n merged.set(key, value);\n } else {\n merged.delete(key);\n }\n }\n }\n\n if (body !== undefined && this.shouldSerializeAsJson(body) && !merged.has('content-type')) {\n merged.set('content-type', 'application/json');\n }\n\n return merged;\n }\n\n private prepareBody(method: string, data?: unknown): BodyInit | null | undefined {\n if (method === 'GET' || method === 'HEAD' || data === undefined) {\n return undefined;\n }\n\n if (typeof data === 'string' || data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\n return data as BodyInit;\n }\n\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n return data;\n }\n\n if (typeof FormData !== 'undefined' && data instanceof FormData) {\n return data;\n }\n\n if (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) {\n return data;\n }\n\n if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) {\n return data;\n }\n\n return JSON.stringify(data);\n }\n\n private shouldSerializeAsJson(data: unknown): boolean {\n if (data === null) {\n return true;\n }\n\n if (Array.isArray(data)) {\n return true;\n }\n\n const primitiveTypes = ['string', 'number', 'boolean', 'bigint'];\n if (primitiveTypes.includes(typeof data)) {\n return false;\n }\n\n if (typeof data === 'object') {\n return !(\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data) ||\n (typeof Blob !== 'undefined' && data instanceof Blob) ||\n (typeof FormData !== 'undefined' && data instanceof FormData) ||\n (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) ||\n (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) ||\n (typeof Buffer !== 'undefined' && Buffer.isBuffer(data))\n );\n }\n\n return false;\n }\n\n private headersToRecord(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n // Safe: key comes from Headers iterator, not user input\n // eslint-disable-next-line security/detect-object-injection\n result[key] = value;\n });\n return result;\n }\n\n private buildUrl(path: string, params?: Record<string, string | number | boolean | null | undefined>): string {\n const absolute = this.isAbsoluteUrl(path) ? path : this.combineWithBase(path);\n if (!params) {\n return absolute;\n }\n\n const url = new URL(absolute);\n for (const [key, rawValue] of Object.entries(params)) {\n if (rawValue === undefined || rawValue === null) {\n continue;\n }\n url.searchParams.set(key, String(rawValue));\n }\n return url.toString();\n }\n\n private isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n }\n\n private combineWithBase(path: string): string {\n if (!this.baseURL) {\n return path;\n }\n\n const normalizedBase = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${normalizedBase}${normalizedPath}`;\n }\n\n private async parseResponse<T>(response: Response, responseType: 'json' | 'text'): Promise<T> {\n if (responseType === 'text') {\n return (await response.text()) as T;\n }\n\n const body = await response.text();\n if (!body) {\n return undefined as T;\n }\n\n try {\n return JSON.parse(body) as T;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to parse JSON response: ${reason}`);\n }\n }\n\n private async createHttpError(response: Response): Promise<ApiRequesterHttpError> {\n const headers = this.headersToRecord(response.headers);\n let data: unknown = undefined;\n let message = `Request failed with status ${response.status}`;\n\n try {\n const text = await response.text();\n if (text) {\n const contentType = headers['content-type'] ?? '';\n if (contentType.includes('application/json')) {\n data = JSON.parse(text);\n if (data && typeof data === 'object' && 'message' in (data as Record<string, unknown>)) {\n const potentialMessage = (data as Record<string, unknown>).message;\n if (typeof potentialMessage === 'string') {\n message = potentialMessage;\n }\n }\n } else {\n data = text;\n message = text;\n }\n }\n } catch {\n // If parsing fails, use defaults set above\n data = undefined;\n message = `Request failed with status ${response.status}`;\n }\n\n return new ApiRequesterHttpError(message, {\n status: response.status,\n statusText: response.statusText,\n data,\n headers,\n });\n }\n\n private handleError(error: unknown): never {\n if (error instanceof ApiRequesterHttpError) {\n console.error('HTTP error:', {\n status: error.status,\n statusText: error.statusText,\n data: error.data,\n });\n } else {\n console.error('Unexpected error:', error);\n }\n\n throw error;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { safeSerializeError } from '../error/error-reporter.js';\n\nexport interface ApiRequestConfig extends Omit<RequestInit, 'method' | 'body' | 'headers'> {\n headers?: Record<string, string | undefined>;\n params?: Record<string, string | number | boolean | null | undefined>;\n responseType?: 'json' | 'text';\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nclass ApiRequesterHttpError extends Error {\n public readonly status: number;\n public readonly statusText: string;\n public readonly data: unknown;\n public readonly headers: Record<string, string>;\n\n constructor(\n message: string,\n options: {\n status: number;\n statusText: string;\n data: unknown;\n headers: Record<string, string>;\n },\n ) {\n super(message);\n this.name = 'ApiRequesterHttpError';\n this.status = options.status;\n this.statusText = options.statusText;\n this.data = options.data;\n this.headers = options.headers;\n }\n}\n\nexport default class ApiRequester {\n private readonly baseURL: string;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(baseURL: string, headers: Record<string, string> = {}) {\n this.baseURL = baseURL;\n this.defaultHeaders = { ...headers };\n }\n\n public async get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('GET', url, undefined, config);\n }\n\n public async post<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('POST', url, data, config);\n }\n\n public async put<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('PUT', url, data, config);\n }\n\n public async delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('DELETE', url, undefined, config);\n }\n\n private async request<T>(\n method: string,\n url: string,\n data?: unknown,\n config?: ApiRequestConfig,\n ): Promise<ApiResponse<T> | undefined> {\n try {\n const { headers: configHeaders, params, responseType = 'json', ...init } = config ?? {};\n const finalUrl = this.buildUrl(url, params);\n const headers = this.mergeHeaders(configHeaders, data);\n const body = this.prepareBody(method, data);\n\n // fetch is a global in Node.js 18+\n // eslint-disable-next-line no-undef\n const response = await fetch(finalUrl, {\n ...init,\n method,\n headers,\n body,\n });\n\n if (!response.ok) {\n throw await this.createHttpError(response);\n }\n\n const parsed = await this.parseResponse<T>(response, responseType);\n\n return {\n data: parsed,\n status: response.status,\n statusText: response.statusText,\n headers: this.headersToRecord(response.headers),\n };\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private mergeHeaders(headers?: Record<string, string | undefined>, body?: unknown): Headers {\n const merged = new Headers();\n\n for (const [key, value] of Object.entries(this.defaultHeaders)) {\n if (value !== undefined) {\n merged.set(key, value);\n }\n }\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n merged.set(key, value);\n } else {\n merged.delete(key);\n }\n }\n }\n\n if (body !== undefined && this.shouldSerializeAsJson(body) && !merged.has('content-type')) {\n merged.set('content-type', 'application/json');\n }\n\n return merged;\n }\n\n private prepareBody(method: string, data?: unknown): BodyInit | null | undefined {\n if (method === 'GET' || method === 'HEAD' || data === undefined) {\n return undefined;\n }\n\n if (typeof data === 'string' || data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\n return data as BodyInit;\n }\n\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n return data;\n }\n\n if (typeof FormData !== 'undefined' && data instanceof FormData) {\n return data;\n }\n\n if (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) {\n return data;\n }\n\n if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) {\n return data;\n }\n\n return JSON.stringify(data);\n }\n\n private shouldSerializeAsJson(data: unknown): boolean {\n if (data === null) {\n return true;\n }\n\n if (Array.isArray(data)) {\n return true;\n }\n\n const primitiveTypes = ['string', 'number', 'boolean', 'bigint'];\n if (primitiveTypes.includes(typeof data)) {\n return false;\n }\n\n if (typeof data === 'object') {\n return !(\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data) ||\n (typeof Blob !== 'undefined' && data instanceof Blob) ||\n (typeof FormData !== 'undefined' && data instanceof FormData) ||\n (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) ||\n (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) ||\n (typeof Buffer !== 'undefined' && Buffer.isBuffer(data))\n );\n }\n\n return false;\n }\n\n private headersToRecord(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n // Safe: key comes from Headers iterator, not user input\n // eslint-disable-next-line security/detect-object-injection\n result[key] = value;\n });\n return result;\n }\n\n private buildUrl(path: string, params?: Record<string, string | number | boolean | null | undefined>): string {\n const absolute = this.isAbsoluteUrl(path) ? path : this.combineWithBase(path);\n if (!params) {\n return absolute;\n }\n\n const url = new URL(absolute);\n for (const [key, rawValue] of Object.entries(params)) {\n if (rawValue === undefined || rawValue === null) {\n continue;\n }\n url.searchParams.set(key, String(rawValue));\n }\n return url.toString();\n }\n\n private isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n }\n\n private combineWithBase(path: string): string {\n if (!this.baseURL) {\n return path;\n }\n\n const normalizedBase = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${normalizedBase}${normalizedPath}`;\n }\n\n private async parseResponse<T>(response: Response, responseType: 'json' | 'text'): Promise<T> {\n if (responseType === 'text') {\n return (await response.text()) as T;\n }\n\n const body = await response.text();\n if (!body) {\n return undefined as T;\n }\n\n try {\n return JSON.parse(body) as T;\n } catch (error) {\n const reason = error instanceof Error ? error.message : safeSerializeError(error);\n throw new Error(`Failed to parse JSON response: ${reason}`);\n }\n }\n\n private async createHttpError(response: Response): Promise<ApiRequesterHttpError> {\n const headers = this.headersToRecord(response.headers);\n let data: unknown = undefined;\n let message = `Request failed with status ${response.status}`;\n\n try {\n const text = await response.text();\n if (text) {\n const contentType = headers['content-type'] ?? '';\n if (contentType.includes('application/json')) {\n data = JSON.parse(text);\n if (data && typeof data === 'object' && 'message' in (data as Record<string, unknown>)) {\n const potentialMessage = (data as Record<string, unknown>).message;\n if (typeof potentialMessage === 'string') {\n message = potentialMessage;\n }\n }\n } else {\n data = text;\n message = text;\n }\n }\n } catch {\n // If parsing fails, use defaults set above\n data = undefined;\n message = `Request failed with status ${response.status}`;\n }\n\n return new ApiRequesterHttpError(message, {\n status: response.status,\n statusText: response.statusText,\n data,\n headers,\n });\n }\n\n private handleError(error: unknown): never {\n if (error instanceof ApiRequesterHttpError) {\n console.error('HTTP error:', {\n status: error.status,\n statusText: error.statusText,\n data: error.data,\n });\n } else {\n console.error('Unexpected error:', error);\n }\n\n throw error;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"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"}
@@ -17,6 +17,7 @@ import { PerformanceMonitorPlugin } from "../performance/performance-monitor.plu
17
17
  import { LifecycleManager, ShutdownController } from "../lifecycle/index.js";
18
18
  import { ConfigValidationError, formatConfigIssues, validateFrameworkConfig } from "../config/schema.js";
19
19
  import { requestExit } from "../lifecycle/exit.js";
20
+ import { safeSerializeError } from "../error/error-reporter.js";
20
21
  class BaseApplication {
21
22
  static {
22
23
  __name(this, "BaseApplication");
@@ -263,7 +264,7 @@ ${formatted}`);
263
264
  }
264
265
  } catch (error) {
265
266
  Logger.error({
266
- error: error instanceof Error ? error : new Error(String(error)),
267
+ error: error instanceof Error ? error : new Error(safeSerializeError(error)),
267
268
  message: "startInstance failure"
268
269
  });
269
270
  throw error;
@@ -336,7 +337,7 @@ ${formatted}`);
336
337
  }
337
338
  } catch (error) {
338
339
  Logger.error({
339
- error: error instanceof Error ? error : new Error(String(error)),
340
+ error: error instanceof Error ? error : new Error(safeSerializeError(error)),
340
341
  message: "Error during graceful shutdown"
341
342
  });
342
343
  this.finalizeExit({ code: 1, reason: "graceful-shutdown-error", error });
@@ -371,7 +372,7 @@ ${formatted}`);
371
372
  }
372
373
  } catch (error) {
373
374
  Logger.error({
374
- error: error instanceof Error ? error : new Error(String(error)),
375
+ error: error instanceof Error ? error : new Error(safeSerializeError(error)),
375
376
  message: "Error during shutdown"
376
377
  });
377
378
  this.finalizeExit({ code: 1, reason: "shutdown-error", error });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/application/base-application.ts"],
4
- "sourcesContent": ["import cluster from 'cluster';\nimport { existsSync, readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join, resolve } from 'path';\nimport { type DatabaseInstance, DatabaseManager } from '../database/index.js';\nimport QueueManager from '../queue/manager.js';\nimport RedisManager from '../redis/manager.js';\nimport type {\n ApplicationConfig,\n ApplicationStartInstanceOptions,\n ApplicationStopInstanceOptions,\n} from './base-application.interface.js';\nimport ClusterManager from '../cluster/cluster-manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport { OS, Time } from '../util/index.js';\nimport CacheManager from '../cache/manager.js';\nimport os from 'os';\nimport EventManager from '../event/manager.js';\nimport Logger from '../logger/logger.js';\nimport type { PerformanceMonitor } from '../performance/performance-monitor.js';\n// Performance monitoring now pluginized\nimport { PerformanceMonitorPlugin } from '../performance/performance-monitor.plugin.js';\nimport { type LifecycleConfig, LifecycleManager, ShutdownController } from '../lifecycle/index.js';\nimport { ConfigValidationError, formatConfigIssues, validateFrameworkConfig } from '../config/schema.js';\nimport { type ExitOutcome, requestExit } from '../lifecycle/exit.js';\n\n// Re-export types for external use\nexport type { ApplicationConfig } from './base-application.interface.js';\n\nexport default abstract class BaseApplication {\n /** Unique instance ID */\n public uniqueInstanceId: string;\n\n /** Application start time */\n protected startTime: number = 0;\n\n /** Shutdown timeout (30 seconds) */\n protected shutdownTimeout = 30000;\n\n /** Cache for application version to avoid repeated imports */\n private static applicationVersionCache: string | undefined;\n\n /** Cluster worker ID */\n protected workerId = cluster.isWorker && cluster.worker ? cluster.worker.id : null;\n\n /** Application config */\n protected config: ApplicationConfig;\n\n /** Application version */\n protected applicationVersion?: string;\n\n /** Redis manager */\n public redisManager: RedisManager;\n\n /** Cache manager */\n public cacheManager: CacheManager;\n\n /** Database manager */\n public databaseManager?: DatabaseManager;\n\n /** Queue manager */\n public queueManager?: QueueManager;\n\n /** Event manager */\n public eventManager?: EventManager;\n\n /** Performance monitor */\n public performanceMonitor?: PerformanceMonitor;\n\n /** Lifecycle manager */\n public lifecycle: LifecycleManager;\n\n /** Shutdown controller */\n public shutdownController: ShutdownController;\n\n public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n // Validate configuration early (fail-fast before side effects)\n try {\n const validated = validateFrameworkConfig(config as any);\n config = validated as unknown as ApplicationConfig;\n } catch (err) {\n if (err instanceof ConfigValidationError) {\n const formatted = formatConfigIssues(err.issues);\n throw new Error(`Configuration validation failed:\\n${formatted}`);\n }\n throw err;\n }\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n this.config = config;\n\n // Configure logger with application settings\n if (this.config.log?.showRequestIdInConsole !== undefined) {\n Logger.configure({ showRequestIdInConsole: this.config.log.showRequestIdInConsole });\n }\n\n // Initialize lifecycle management\n const lifecycleConfig: Partial<LifecycleConfig> = {\n gracefulShutdown: {\n timeoutMs: this.shutdownTimeout,\n },\n readiness: {\n timeoutMs: 30000,\n checkIntervalMs: 100,\n },\n };\n this.lifecycle = new LifecycleManager(lifecycleConfig);\n this.shutdownController = new ShutdownController(this.lifecycle);\n\n // Register shutdown hooks for cleanup\n this.registerShutdownHooks();\n\n // Initialize Redis manager\n this.redisManager = new RedisManager({\n applicationConfig: this.config,\n host: this.config.redis.host,\n port: this.config.redis.port,\n password: this.config.redis.password,\n });\n\n // Initialize cache manager\n this.cacheManager = new CacheManager({\n redisManager: this.redisManager,\n });\n\n // Register performance monitor plugin (idempotent & opt-in)\n PerformanceMonitorPlugin.register(this);\n\n // Set up global error handlers\n this.setupGlobalErrorHandlers();\n\n if (this.config.database && this.config.database.enabled === true) {\n const defaultEntitiesDirectory = join(this.config.rootDirectory, 'src', 'database', 'entities');\n\n if (!this.config.database.entitiesDirectory) {\n this.config.database.entitiesDirectory = defaultEntitiesDirectory;\n }\n\n if (!existsSync(this.config.database.entitiesDirectory)) {\n throw new Error(`Database entities directory not found (Path: ${this.config.database.entitiesDirectory})`);\n }\n\n // Initialize Database manager\n this.databaseManager = new DatabaseManager({\n applicationConfig: this.config,\n host: this.config.database.host,\n port: this.config.database.port,\n username: this.config.database.username,\n password: this.config.database.password,\n databaseName: this.config.database.databaseName,\n entitiesDirectory: this.config.database.entitiesDirectory,\n });\n }\n }\n\n /**\n * Get application version\n */\n public async getApplicationVersion(): Promise<string> {\n // Return cached version if available\n if (BaseApplication.applicationVersionCache !== undefined) {\n return BaseApplication.applicationVersionCache;\n }\n\n // Resolve the path to package.json\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const packageJsonPath = resolve(__dirname, '../../package.json');\n\n // Read and parse the file\n const fileContents = readFileSync(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(fileContents);\n\n if (!packageJson?.version) {\n throw new Error('Application version not found');\n }\n\n // Cache and return the version\n BaseApplication.applicationVersionCache = packageJson.version;\n\n return packageJson.version;\n }\n\n /**\n * Start application\n */\n public async start(): Promise<void> {\n // Start application timer\n this.startTime = Time.now();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\n onStopped: this.onStopped.bind(this),\n };\n\n if (this.config.cluster?.enabled) {\n // Initialize clustered application\n const clusterManager = new ClusterManager({\n config: this.config.cluster,\n\n startApplicationCallback: () => this.startInstance(startInstanceOptions),\n stopApplicationCallback: () => this.stop(stopInstanceOptions),\n });\n\n // Start cluster\n clusterManager.start();\n } else {\n // Start standalone application\n await this.startInstance(startInstanceOptions);\n\n // Note: Signal handling should be implemented at the application launcher level\n // The lifecycle manager provides the stop() method for programmatic shutdown\n }\n }\n\n /**\n * Before application start\n */\n private async onBeforeStart(): Promise<{\n redisInstance: RedisInstance;\n databaseInstance: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager;\n }> {\n // Connect to Redis\n const redisInstance = await this.redisManager.connect();\n\n // Connect to database\n const databaseInstance = this.databaseManager ? await this.databaseManager.connect() : null;\n\n let eventManager: EventManager | undefined;\n\n if (this.config.event?.enabled) {\n eventManager = new EventManager({\n applicationConfig: this.config,\n options: this.config.event,\n events: this.config.event.events || [],\n redisInstance,\n databaseInstance,\n // queueManager,\n });\n\n eventManager.load();\n }\n\n // Initialize queue\n const queueManager = new QueueManager({\n applicationConfig: this.config,\n options: {\n processorsDirectory: this.config.queue.processorsDirectory,\n },\n queues: this.config.queue.queues,\n redisInstance,\n databaseInstance,\n eventManager,\n });\n\n // Register queues\n await queueManager.registerQueues({\n queues: this.config.queue.queues,\n });\n\n // Register readiness checks for key services\n this.lifecycle.addReadinessCheck('redis', async () => {\n try {\n return await redisInstance.isConnected();\n } catch {\n return false;\n }\n });\n\n if (databaseInstance) {\n this.lifecycle.addReadinessCheck('database', async () => {\n try {\n return await databaseInstance.isConnected();\n } catch {\n return false;\n }\n });\n }\n\n return {\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n };\n }\n\n /**\n * Application started event\n */\n protected onStarted({ startupTime: _startupTime }: { startupTime: number }): void {}\n\n /**\n * Application stopped event\n */\n protected onStopped({ runtime: _runtime }: { runtime: number }): void {}\n\n /**\n * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Phase 1: Initialize (resource setup)\n const initResult = await this.lifecycle.initialize();\n if (initResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle init phase encountered errors',\n meta: { errors: initResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Phase 2: Start (component startup)\n const startResult = await this.lifecycle.start();\n if (startResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle start phase encountered errors',\n meta: { errors: startResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Phase 3: Ready (application accepting traffic)\n const readyResult = await this.lifecycle.ready();\n if (readyResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle ready phase encountered errors',\n meta: { errors: readyResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTimeMs({\n startTime: this.startTime,\n });\n\n // On application started\n if (options.onStarted) {\n await options.onStarted({ startupTime });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'startInstance failure',\n });\n throw error;\n }\n }\n\n protected abstract startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n }: {\n redisInstance: RedisInstance;\n databaseInstance?: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager | null;\n }): Promise<void>;\n\n protected abstract stopCallback(): void;\n\n /**\n * Set up global error handlers\n */\n /**\n * Initialize performance monitor\n */\n // initializePerformanceMonitor deprecated in favor of PerformanceMonitorPlugin\n // (left intentionally absent)\n\n private setupGlobalErrorHandlers(): void {\n // Handle uncaught exceptions\n process.on('uncaughtException', error => {\n Logger.error({ error, message: 'Uncaught Exception' });\n this.initiateGracefulShutdown();\n });\n\n // Handle unhandled promise rejections\n process.on('unhandledRejection', (reason, promise) => {\n Logger.error({\n error: reason instanceof Error ? reason : new Error(String(reason)),\n message: 'Unhandled Rejection',\n meta: { promise: String(promise) },\n });\n this.initiateGracefulShutdown();\n });\n }\n\n /**\n * Register shutdown hooks for proper cleanup\n */\n private registerShutdownHooks(): void {\n // Register shutdown hooks in reverse dependency order\n this.lifecycle.onShutdown(async () => {\n Logger.info({ message: 'Executing custom stop callback' });\n await this.stopCallback();\n });\n\n this.lifecycle.onShutdown(async () => {\n if (this.redisManager) {\n Logger.info({ message: 'Disconnecting from Redis' });\n await this.redisManager.disconnect();\n }\n });\n\n this.lifecycle.onShutdown(async () => {\n if (this.databaseManager) {\n Logger.info({ message: 'Disconnecting from database' });\n await this.databaseManager.disconnect();\n }\n });\n\n // Performance monitor is handled via trackInterval, so it will be cleaned up automatically\n }\n\n /**\n * Initiate graceful shutdown\n */\n private async initiateGracefulShutdown(): Promise<void> {\n if (this.shutdownController.isShuttingDown) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n try {\n const result = await this.shutdownController.initiate('error-triggered');\n if (result.errors.length > 0) {\n Logger.error({\n message: 'Errors during shutdown',\n error: result.errors,\n });\n this.finalizeExit({ code: 1, reason: 'graceful-shutdown-error', error: result.errors });\n } else if (result.timedOut) {\n Logger.warn({ message: 'Shutdown timed out' });\n this.finalizeExit({ code: 1, reason: 'shutdown-timeout' });\n } else {\n this.finalizeExit({ code: 0, reason: 'error-shutdown-complete' });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during graceful shutdown',\n });\n this.finalizeExit({ code: 1, reason: 'graceful-shutdown-error', error });\n }\n }\n\n /**\n * Stop application using lifecycle manager\n */\n public async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.shutdownController.isShuttingDown) {\n return;\n }\n\n // Register the onStopped callback if provided\n if (onStopped) {\n this.lifecycle.onShutdown(() => {\n const runtime = process.uptime() * 1000;\n onStopped({ runtime });\n });\n }\n\n try {\n const result = await this.shutdownController.initiate('manual-stop');\n if (result.errors.length > 0) {\n Logger.error({\n message: 'Errors during shutdown',\n error: result.errors,\n });\n this.finalizeExit({ code: 1, reason: 'shutdown-error', error: result.errors });\n } else if (result.timedOut) {\n Logger.warn({ message: 'Shutdown timed out' });\n this.finalizeExit({ code: 1, reason: 'shutdown-timeout' });\n } else {\n this.finalizeExit({ code: 0, reason: 'shutdown-complete' });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during shutdown',\n });\n this.finalizeExit({ code: 1, reason: 'shutdown-error', error });\n }\n }\n\n /**\n * Finalize exit: during tests, suppress actual process exit to avoid failing vitest runs.\n */\n private finalizeExit(outcome: ExitOutcome): void {\n const nodeEnv = process.env.NODE_ENV ?? '';\n const isTestEnv =\n nodeEnv.toLowerCase() === 'test' ||\n 'VITEST' in process.env ||\n 'VITEST_WORKER_ID' in process.env ||\n process.argv.some(a => a.includes('vitest')) ||\n typeof (globalThis as any).afterAll === 'function';\n\n if (isTestEnv) {\n Logger.info({ message: `Skipping process exit in test environment (${outcome.reason})`, code: outcome.code });\n return;\n }\n requestExit(outcome);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import cluster from 'cluster';\nimport { existsSync, readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join, resolve } from 'path';\nimport { type DatabaseInstance, DatabaseManager } from '../database/index.js';\nimport QueueManager from '../queue/manager.js';\nimport RedisManager from '../redis/manager.js';\nimport type {\n ApplicationConfig,\n ApplicationStartInstanceOptions,\n ApplicationStopInstanceOptions,\n} from './base-application.interface.js';\nimport ClusterManager from '../cluster/cluster-manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport { OS, Time } from '../util/index.js';\nimport CacheManager from '../cache/manager.js';\nimport os from 'os';\nimport EventManager from '../event/manager.js';\nimport Logger from '../logger/logger.js';\nimport type { PerformanceMonitor } from '../performance/performance-monitor.js';\n// Performance monitoring now pluginized\nimport { PerformanceMonitorPlugin } from '../performance/performance-monitor.plugin.js';\nimport { type LifecycleConfig, LifecycleManager, ShutdownController } from '../lifecycle/index.js';\nimport { ConfigValidationError, formatConfigIssues, validateFrameworkConfig } from '../config/schema.js';\nimport { type ExitOutcome, requestExit } from '../lifecycle/exit.js';\nimport { safeSerializeError } from '../error/error-reporter.js';\n\n// Re-export types for external use\nexport type { ApplicationConfig } from './base-application.interface.js';\n\nexport default abstract class BaseApplication {\n /** Unique instance ID */\n public uniqueInstanceId: string;\n\n /** Application start time */\n protected startTime: number = 0;\n\n /** Shutdown timeout (30 seconds) */\n protected shutdownTimeout = 30000;\n\n /** Cache for application version to avoid repeated imports */\n private static applicationVersionCache: string | undefined;\n\n /** Cluster worker ID */\n protected workerId = cluster.isWorker && cluster.worker ? cluster.worker.id : null;\n\n /** Application config */\n protected config: ApplicationConfig;\n\n /** Application version */\n protected applicationVersion?: string;\n\n /** Redis manager */\n public redisManager: RedisManager;\n\n /** Cache manager */\n public cacheManager: CacheManager;\n\n /** Database manager */\n public databaseManager?: DatabaseManager;\n\n /** Queue manager */\n public queueManager?: QueueManager;\n\n /** Event manager */\n public eventManager?: EventManager;\n\n /** Performance monitor */\n public performanceMonitor?: PerformanceMonitor;\n\n /** Lifecycle manager */\n public lifecycle: LifecycleManager;\n\n /** Shutdown controller */\n public shutdownController: ShutdownController;\n\n public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n // Validate configuration early (fail-fast before side effects)\n try {\n const validated = validateFrameworkConfig(config as any);\n config = validated as unknown as ApplicationConfig;\n } catch (err) {\n if (err instanceof ConfigValidationError) {\n const formatted = formatConfigIssues(err.issues);\n throw new Error(`Configuration validation failed:\\n${formatted}`);\n }\n throw err;\n }\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n this.config = config;\n\n // Configure logger with application settings\n if (this.config.log?.showRequestIdInConsole !== undefined) {\n Logger.configure({ showRequestIdInConsole: this.config.log.showRequestIdInConsole });\n }\n\n // Initialize lifecycle management\n const lifecycleConfig: Partial<LifecycleConfig> = {\n gracefulShutdown: {\n timeoutMs: this.shutdownTimeout,\n },\n readiness: {\n timeoutMs: 30000,\n checkIntervalMs: 100,\n },\n };\n this.lifecycle = new LifecycleManager(lifecycleConfig);\n this.shutdownController = new ShutdownController(this.lifecycle);\n\n // Register shutdown hooks for cleanup\n this.registerShutdownHooks();\n\n // Initialize Redis manager\n this.redisManager = new RedisManager({\n applicationConfig: this.config,\n host: this.config.redis.host,\n port: this.config.redis.port,\n password: this.config.redis.password,\n });\n\n // Initialize cache manager\n this.cacheManager = new CacheManager({\n redisManager: this.redisManager,\n });\n\n // Register performance monitor plugin (idempotent & opt-in)\n PerformanceMonitorPlugin.register(this);\n\n // Set up global error handlers\n this.setupGlobalErrorHandlers();\n\n if (this.config.database && this.config.database.enabled === true) {\n const defaultEntitiesDirectory = join(this.config.rootDirectory, 'src', 'database', 'entities');\n\n if (!this.config.database.entitiesDirectory) {\n this.config.database.entitiesDirectory = defaultEntitiesDirectory;\n }\n\n if (!existsSync(this.config.database.entitiesDirectory)) {\n throw new Error(`Database entities directory not found (Path: ${this.config.database.entitiesDirectory})`);\n }\n\n // Initialize Database manager\n this.databaseManager = new DatabaseManager({\n applicationConfig: this.config,\n host: this.config.database.host,\n port: this.config.database.port,\n username: this.config.database.username,\n password: this.config.database.password,\n databaseName: this.config.database.databaseName,\n entitiesDirectory: this.config.database.entitiesDirectory,\n });\n }\n }\n\n /**\n * Get application version\n */\n public async getApplicationVersion(): Promise<string> {\n // Return cached version if available\n if (BaseApplication.applicationVersionCache !== undefined) {\n return BaseApplication.applicationVersionCache;\n }\n\n // Resolve the path to package.json\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const packageJsonPath = resolve(__dirname, '../../package.json');\n\n // Read and parse the file\n const fileContents = readFileSync(packageJsonPath, 'utf-8');\n const packageJson = JSON.parse(fileContents);\n\n if (!packageJson?.version) {\n throw new Error('Application version not found');\n }\n\n // Cache and return the version\n BaseApplication.applicationVersionCache = packageJson.version;\n\n return packageJson.version;\n }\n\n /**\n * Start application\n */\n public async start(): Promise<void> {\n // Start application timer\n this.startTime = Time.now();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\n onStopped: this.onStopped.bind(this),\n };\n\n if (this.config.cluster?.enabled) {\n // Initialize clustered application\n const clusterManager = new ClusterManager({\n config: this.config.cluster,\n\n startApplicationCallback: () => this.startInstance(startInstanceOptions),\n stopApplicationCallback: () => this.stop(stopInstanceOptions),\n });\n\n // Start cluster\n clusterManager.start();\n } else {\n // Start standalone application\n await this.startInstance(startInstanceOptions);\n\n // Note: Signal handling should be implemented at the application launcher level\n // The lifecycle manager provides the stop() method for programmatic shutdown\n }\n }\n\n /**\n * Before application start\n */\n private async onBeforeStart(): Promise<{\n redisInstance: RedisInstance;\n databaseInstance: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager;\n }> {\n // Connect to Redis\n const redisInstance = await this.redisManager.connect();\n\n // Connect to database\n const databaseInstance = this.databaseManager ? await this.databaseManager.connect() : null;\n\n let eventManager: EventManager | undefined;\n\n if (this.config.event?.enabled) {\n eventManager = new EventManager({\n applicationConfig: this.config,\n options: this.config.event,\n events: this.config.event.events || [],\n redisInstance,\n databaseInstance,\n // queueManager,\n });\n\n eventManager.load();\n }\n\n // Initialize queue\n const queueManager = new QueueManager({\n applicationConfig: this.config,\n options: {\n processorsDirectory: this.config.queue.processorsDirectory,\n },\n queues: this.config.queue.queues,\n redisInstance,\n databaseInstance,\n eventManager,\n });\n\n // Register queues\n await queueManager.registerQueues({\n queues: this.config.queue.queues,\n });\n\n // Register readiness checks for key services\n this.lifecycle.addReadinessCheck('redis', async () => {\n try {\n return await redisInstance.isConnected();\n } catch {\n return false;\n }\n });\n\n if (databaseInstance) {\n this.lifecycle.addReadinessCheck('database', async () => {\n try {\n return await databaseInstance.isConnected();\n } catch {\n return false;\n }\n });\n }\n\n return {\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n };\n }\n\n /**\n * Application started event\n */\n protected onStarted({ startupTime: _startupTime }: { startupTime: number }): void {}\n\n /**\n * Application stopped event\n */\n protected onStopped({ runtime: _runtime }: { runtime: number }): void {}\n\n /**\n * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Phase 1: Initialize (resource setup)\n const initResult = await this.lifecycle.initialize();\n if (initResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle init phase encountered errors',\n meta: { errors: initResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Phase 2: Start (component startup)\n const startResult = await this.lifecycle.start();\n if (startResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle start phase encountered errors',\n meta: { errors: startResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Phase 3: Ready (application accepting traffic)\n const readyResult = await this.lifecycle.ready();\n if (readyResult.errors.length > 0) {\n Logger.warn({\n message: 'Lifecycle ready phase encountered errors',\n meta: { errors: readyResult.errors.map(e => (e instanceof Error ? e.message : String(e))) },\n });\n }\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTimeMs({\n startTime: this.startTime,\n });\n\n // On application started\n if (options.onStarted) {\n await options.onStarted({ startupTime });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(safeSerializeError(error)),\n message: 'startInstance failure',\n });\n throw error;\n }\n }\n\n protected abstract startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n }: {\n redisInstance: RedisInstance;\n databaseInstance?: DatabaseInstance | null;\n queueManager: QueueManager;\n eventManager?: EventManager | null;\n }): Promise<void>;\n\n protected abstract stopCallback(): void;\n\n /**\n * Set up global error handlers\n */\n /**\n * Initialize performance monitor\n */\n // initializePerformanceMonitor deprecated in favor of PerformanceMonitorPlugin\n // (left intentionally absent)\n\n private setupGlobalErrorHandlers(): void {\n // Handle uncaught exceptions\n process.on('uncaughtException', error => {\n Logger.error({ error, message: 'Uncaught Exception' });\n this.initiateGracefulShutdown();\n });\n\n // Handle unhandled promise rejections\n process.on('unhandledRejection', (reason, promise) => {\n Logger.error({\n error: reason instanceof Error ? reason : new Error(String(reason)),\n message: 'Unhandled Rejection',\n meta: { promise: String(promise) },\n });\n this.initiateGracefulShutdown();\n });\n }\n\n /**\n * Register shutdown hooks for proper cleanup\n */\n private registerShutdownHooks(): void {\n // Register shutdown hooks in reverse dependency order\n this.lifecycle.onShutdown(async () => {\n Logger.info({ message: 'Executing custom stop callback' });\n await this.stopCallback();\n });\n\n this.lifecycle.onShutdown(async () => {\n if (this.redisManager) {\n Logger.info({ message: 'Disconnecting from Redis' });\n await this.redisManager.disconnect();\n }\n });\n\n this.lifecycle.onShutdown(async () => {\n if (this.databaseManager) {\n Logger.info({ message: 'Disconnecting from database' });\n await this.databaseManager.disconnect();\n }\n });\n\n // Performance monitor is handled via trackInterval, so it will be cleaned up automatically\n }\n\n /**\n * Initiate graceful shutdown\n */\n private async initiateGracefulShutdown(): Promise<void> {\n if (this.shutdownController.isShuttingDown) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n try {\n const result = await this.shutdownController.initiate('error-triggered');\n if (result.errors.length > 0) {\n Logger.error({\n message: 'Errors during shutdown',\n error: result.errors,\n });\n this.finalizeExit({ code: 1, reason: 'graceful-shutdown-error', error: result.errors });\n } else if (result.timedOut) {\n Logger.warn({ message: 'Shutdown timed out' });\n this.finalizeExit({ code: 1, reason: 'shutdown-timeout' });\n } else {\n this.finalizeExit({ code: 0, reason: 'error-shutdown-complete' });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(safeSerializeError(error)),\n message: 'Error during graceful shutdown',\n });\n this.finalizeExit({ code: 1, reason: 'graceful-shutdown-error', error });\n }\n }\n\n /**\n * Stop application using lifecycle manager\n */\n public async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.shutdownController.isShuttingDown) {\n return;\n }\n\n // Register the onStopped callback if provided\n if (onStopped) {\n this.lifecycle.onShutdown(() => {\n const runtime = process.uptime() * 1000;\n onStopped({ runtime });\n });\n }\n\n try {\n const result = await this.shutdownController.initiate('manual-stop');\n if (result.errors.length > 0) {\n Logger.error({\n message: 'Errors during shutdown',\n error: result.errors,\n });\n this.finalizeExit({ code: 1, reason: 'shutdown-error', error: result.errors });\n } else if (result.timedOut) {\n Logger.warn({ message: 'Shutdown timed out' });\n this.finalizeExit({ code: 1, reason: 'shutdown-timeout' });\n } else {\n this.finalizeExit({ code: 0, reason: 'shutdown-complete' });\n }\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(safeSerializeError(error)),\n message: 'Error during shutdown',\n });\n this.finalizeExit({ code: 1, reason: 'shutdown-error', error });\n }\n }\n\n /**\n * Finalize exit: during tests, suppress actual process exit to avoid failing vitest runs.\n */\n private finalizeExit(outcome: ExitOutcome): void {\n const nodeEnv = process.env.NODE_ENV ?? '';\n const isTestEnv =\n nodeEnv.toLowerCase() === 'test' ||\n 'VITEST' in process.env ||\n 'VITEST_WORKER_ID' in process.env ||\n process.argv.some(a => a.includes('vitest')) ||\n typeof (globalThis as any).afterAll === 'function';\n\n if (isTestEnv) {\n Logger.info({ message: `Skipping process exit in test environment (${outcome.reason})`, code: outcome.code });\n return;\n }\n requestExit(outcome);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/cache/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAC,CAAgB;gBAE1B,EAAE,YAAY,EAAE,EAAE,iBAAiB;IAI/C;;;OAGG;YACW,gBAAgB;IAa9B;;;;;;;;;;;;;;OAcG;IACU,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAkBpE;;;;;;;;;;;;;;;;;;;OAmBG;IACU,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9G;;;;;;;;;OASG;IACU,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGpC"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/cache/manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,qBAAqB,CAAC;AAIpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,aAAa,CAAC,CAAgB;gBAE1B,EAAE,YAAY,EAAE,EAAE,iBAAiB;IAI/C;;;OAGG;YACW,gBAAgB;IAa9B;;;;;;;;;;;;;;OAcG;IACU,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAkBpE;;;;;;;;;;;;;;;;;;;OAmBG;IACU,OAAO,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,CAAC,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAK9G;;;;;;;;;OASG;IACU,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAK/D;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAGpC"}
@@ -1,5 +1,6 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { safeSerializeError } from "../error/error-reporter.js";
3
4
  class CacheManager {
4
5
  static {
5
6
  __name(this, "CacheManager");
@@ -47,7 +48,7 @@ class CacheManager {
47
48
  try {
48
49
  return JSON.parse(raw);
49
50
  } catch (error) {
50
- const errorMessage = error instanceof Error ? error.message : String(error);
51
+ const errorMessage = error instanceof Error ? error.message : safeSerializeError(error);
51
52
  throw new Error(`Failed to parse cached value for key "${key}": ${errorMessage}`);
52
53
  }
53
54
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cache/manager.ts"],
4
- "sourcesContent": ["import type RedisManager from '../redis/manager.js';\nimport type RedisInstance from '../redis/instance.js';\n\n/**\n * CacheManager\n *\n * Thin abstraction over Redis for basic JSON value caching. Unifies all Redis\n * access through the framework RedisManager / RedisInstance (ioredis) so we\n * avoid maintaining a second client implementation (node-redis).\n *\n * Lazy acquisition: the first call to any cache method will either reuse an\n * existing connected RedisInstance (if already established by application\n * startup) or trigger a connection via RedisManager.\n *\n * **Important:** All values are stored as JSON strings. Only JSON-serializable\n * values are supported. Complex types like Date, Map, Set, RegExp, etc. will\n * lose their type information during serialization:\n * - `Date` objects \u2192 ISO strings\n * - `Map` / `Set` \u2192 empty objects `{}`\n * - `undefined` \u2192 omitted from objects, `null` in arrays\n * - Functions \u2192 omitted\n *\n * @example\n * ```typescript\n * // Supported types\n * await cache.setItem({ key: 'user', value: { id: 1, name: 'John' } }); // \u2713\n * await cache.setItem({ key: 'count', value: 42 }); // \u2713\n * await cache.setItem({ key: 'tags', value: ['a', 'b', 'c'] }); // \u2713\n *\n * // Unsupported types (will lose type information)\n * await cache.setItem({ key: 'date', value: new Date() }); // \u2192 ISO string\n * await cache.setItem({ key: 'map', value: new Map() }); // \u2192 {}\n * ```\n */\n\nexport interface CacheManagerProps {\n /** Redis manager (shared across the application) */\n redisManager: RedisManager;\n}\n\nexport default class CacheManager {\n private redisManager: RedisManager;\n private redisInstance?: RedisInstance;\n\n constructor({ redisManager }: CacheManagerProps) {\n this.redisManager = redisManager;\n }\n\n /**\n * Ensure we have a connected RedisInstance. Reuses the first existing\n * instance if already connected by the application bootstrap.\n */\n private async getRedisInstance(): Promise<RedisInstance> {\n if (this.redisInstance) return this.redisInstance;\n\n if (this.redisManager.instances.length > 0) {\n this.redisInstance = this.redisManager.instances[0];\n return this.redisInstance;\n }\n\n // Lazily connect if no instances yet (e.g., used before app onBeforeStart)\n this.redisInstance = await this.redisManager.connect();\n return this.redisInstance;\n }\n\n /**\n * Get a cached JSON value (deserialized) or null if not present.\n *\n * @param key - Cache key\n * @returns Deserialized value or null if not found\n * @throws {Error} If the cached value is not valid JSON\n *\n * @example\n * ```typescript\n * const user = await cache.getItem<{ id: number; name: string }>({ key: 'user:123' });\n * if (user) {\n * console.log(user.name);\n * }\n * ```\n */\n public async getItem<T>({ key }: { key: string }): Promise<T | null> {\n const instance = await this.getRedisInstance();\n const raw = await instance.getCache({ key });\n if (raw === null) return null;\n\n // Validate that we received a string (Redis should always return string or null)\n if (typeof raw !== 'string') {\n throw new Error(`Cache value for key \"${key}\" must be a string, got ${typeof raw}`);\n }\n\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to parse cached value for key \"${key}\": ${errorMessage}`);\n }\n }\n\n /**\n * Set a JSON-serializable value. Optionally specify lifetime (seconds).\n *\n * @param key - Cache key\n * @param value - Value to cache (must be JSON-serializable)\n * @param lifetime - Optional expiration in seconds\n *\n * @example\n * ```typescript\n * // Cache with no expiration\n * await cache.setItem({ key: 'config', value: { theme: 'dark' } });\n *\n * // Cache with 1 hour expiration\n * await cache.setItem({\n * key: 'session:abc',\n * value: { userId: 123 },\n * lifetime: 3600\n * });\n * ```\n */\n public async setItem<T>({ key, value, lifetime }: { key: string; value: T; lifetime?: number }): Promise<void> {\n const instance = await this.getRedisInstance();\n await instance.setCache({ key, value, expiration: lifetime });\n }\n\n /**\n * Delete a cached value.\n *\n * @param key - Cache key to delete\n *\n * @example\n * ```typescript\n * await cache.clearItem({ key: 'session:abc' });\n * ```\n */\n public async clearItem({ key }: { key: string }): Promise<void> {\n const instance = await this.getRedisInstance();\n await instance.deleteCache({ key });\n }\n\n /**\n * No-op: lifecycle handles Redis disconnection globally.\n */\n public async close(): Promise<void> {\n // Intentionally empty; RedisManager handles disconnect.\n }\n}\n"],
5
- "mappings": ";;AAwCA,MAAO,aAA2B;AAAA,EAxClC,OAwCkC;AAAA;AAAA;AAAA,EACxB;AAAA,EACA;AAAA,EAER,YAAY,EAAE,aAAa,GAAsB;AAC/C,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBAA2C;AACvD,QAAI,KAAK,cAAe,QAAO,KAAK;AAEpC,QAAI,KAAK,aAAa,UAAU,SAAS,GAAG;AAC1C,WAAK,gBAAgB,KAAK,aAAa,UAAU,CAAC;AAClD,aAAO,KAAK;AAAA,IACd;AAGA,SAAK,gBAAgB,MAAM,KAAK,aAAa,QAAQ;AACrD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAa,QAAW,EAAE,IAAI,GAAuC;AACnE,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAC7C,UAAM,MAAM,MAAM,SAAS,SAAS,EAAE,IAAI,CAAC;AAC3C,QAAI,QAAQ,KAAM,QAAO;AAGzB,QAAI,OAAO,QAAQ,UAAU;AAC3B,YAAM,IAAI,MAAM,wBAAwB,GAAG,2BAA2B,OAAO,GAAG,EAAE;AAAA,IACpF;AAEA,QAAI;AACF,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,YAAM,IAAI,MAAM,yCAAyC,GAAG,MAAM,YAAY,EAAE;AAAA,IAClF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAa,QAAW,EAAE,KAAK,OAAO,SAAS,GAAgE;AAC7G,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAC7C,UAAM,SAAS,SAAS,EAAE,KAAK,OAAO,YAAY,SAAS,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAa,UAAU,EAAE,IAAI,GAAmC;AAC9D,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAC7C,UAAM,SAAS,YAAY,EAAE,IAAI,CAAC;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,QAAuB;AAAA,EAEpC;AACF;",
4
+ "sourcesContent": ["import type RedisManager from '../redis/manager.js';\nimport type RedisInstance from '../redis/instance.js';\nimport { safeSerializeError } from '../error/error-reporter.js';\n\n/**\n * CacheManager\n *\n * Thin abstraction over Redis for basic JSON value caching. Unifies all Redis\n * access through the framework RedisManager / RedisInstance (ioredis) so we\n * avoid maintaining a second client implementation (node-redis).\n *\n * Lazy acquisition: the first call to any cache method will either reuse an\n * existing connected RedisInstance (if already established by application\n * startup) or trigger a connection via RedisManager.\n *\n * **Important:** All values are stored as JSON strings. Only JSON-serializable\n * values are supported. Complex types like Date, Map, Set, RegExp, etc. will\n * lose their type information during serialization:\n * - `Date` objects \u2192 ISO strings\n * - `Map` / `Set` \u2192 empty objects `{}`\n * - `undefined` \u2192 omitted from objects, `null` in arrays\n * - Functions \u2192 omitted\n *\n * @example\n * ```typescript\n * // Supported types\n * await cache.setItem({ key: 'user', value: { id: 1, name: 'John' } }); // \u2713\n * await cache.setItem({ key: 'count', value: 42 }); // \u2713\n * await cache.setItem({ key: 'tags', value: ['a', 'b', 'c'] }); // \u2713\n *\n * // Unsupported types (will lose type information)\n * await cache.setItem({ key: 'date', value: new Date() }); // \u2192 ISO string\n * await cache.setItem({ key: 'map', value: new Map() }); // \u2192 {}\n * ```\n */\n\nexport interface CacheManagerProps {\n /** Redis manager (shared across the application) */\n redisManager: RedisManager;\n}\n\nexport default class CacheManager {\n private redisManager: RedisManager;\n private redisInstance?: RedisInstance;\n\n constructor({ redisManager }: CacheManagerProps) {\n this.redisManager = redisManager;\n }\n\n /**\n * Ensure we have a connected RedisInstance. Reuses the first existing\n * instance if already connected by the application bootstrap.\n */\n private async getRedisInstance(): Promise<RedisInstance> {\n if (this.redisInstance) return this.redisInstance;\n\n if (this.redisManager.instances.length > 0) {\n this.redisInstance = this.redisManager.instances[0];\n return this.redisInstance;\n }\n\n // Lazily connect if no instances yet (e.g., used before app onBeforeStart)\n this.redisInstance = await this.redisManager.connect();\n return this.redisInstance;\n }\n\n /**\n * Get a cached JSON value (deserialized) or null if not present.\n *\n * @param key - Cache key\n * @returns Deserialized value or null if not found\n * @throws {Error} If the cached value is not valid JSON\n *\n * @example\n * ```typescript\n * const user = await cache.getItem<{ id: number; name: string }>({ key: 'user:123' });\n * if (user) {\n * console.log(user.name);\n * }\n * ```\n */\n public async getItem<T>({ key }: { key: string }): Promise<T | null> {\n const instance = await this.getRedisInstance();\n const raw = await instance.getCache({ key });\n if (raw === null) return null;\n\n // Validate that we received a string (Redis should always return string or null)\n if (typeof raw !== 'string') {\n throw new Error(`Cache value for key \"${key}\" must be a string, got ${typeof raw}`);\n }\n\n try {\n return JSON.parse(raw) as T;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : safeSerializeError(error);\n throw new Error(`Failed to parse cached value for key \"${key}\": ${errorMessage}`);\n }\n }\n\n /**\n * Set a JSON-serializable value. Optionally specify lifetime (seconds).\n *\n * @param key - Cache key\n * @param value - Value to cache (must be JSON-serializable)\n * @param lifetime - Optional expiration in seconds\n *\n * @example\n * ```typescript\n * // Cache with no expiration\n * await cache.setItem({ key: 'config', value: { theme: 'dark' } });\n *\n * // Cache with 1 hour expiration\n * await cache.setItem({\n * key: 'session:abc',\n * value: { userId: 123 },\n * lifetime: 3600\n * });\n * ```\n */\n public async setItem<T>({ key, value, lifetime }: { key: string; value: T; lifetime?: number }): Promise<void> {\n const instance = await this.getRedisInstance();\n await instance.setCache({ key, value, expiration: lifetime });\n }\n\n /**\n * Delete a cached value.\n *\n * @param key - Cache key to delete\n *\n * @example\n * ```typescript\n * await cache.clearItem({ key: 'session:abc' });\n * ```\n */\n public async clearItem({ key }: { key: string }): Promise<void> {\n const instance = await this.getRedisInstance();\n await instance.deleteCache({ key });\n }\n\n /**\n * No-op: lifecycle handles Redis disconnection globally.\n */\n public async close(): Promise<void> {\n // Intentionally empty; RedisManager handles disconnect.\n }\n}\n"],
5
+ "mappings": ";;AAEA,SAAS,0BAA0B;AAuCnC,MAAO,aAA2B;AAAA,EAzClC,OAyCkC;AAAA;AAAA;AAAA,EACxB;AAAA,EACA;AAAA,EAER,YAAY,EAAE,aAAa,GAAsB;AAC/C,SAAK,eAAe;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,mBAA2C;AACvD,QAAI,KAAK,cAAe,QAAO,KAAK;AAEpC,QAAI,KAAK,aAAa,UAAU,SAAS,GAAG;AAC1C,WAAK,gBAAgB,KAAK,aAAa,UAAU,CAAC;AAClD,aAAO,KAAK;AAAA,IACd;AAGA,SAAK,gBAAgB,MAAM,KAAK,aAAa,QAAQ;AACrD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAa,QAAW,EAAE,IAAI,GAAuC;AACnE,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAC7C,UAAM,MAAM,MAAM,SAAS,SAAS,EAAE,IAAI,CAAC;AAC3C,QAAI,QAAQ,KAAM,QAAO;AAGzB,QAAI,OAAO,QAAQ,UAAU;AAC3B,YAAM,IAAI,MAAM,wBAAwB,GAAG,2BAA2B,OAAO,GAAG,EAAE;AAAA,IACpF;AAEA,QAAI;AACF,aAAO,KAAK,MAAM,GAAG;AAAA,IACvB,SAAS,OAAO;AACd,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,mBAAmB,KAAK;AACtF,YAAM,IAAI,MAAM,yCAAyC,GAAG,MAAM,YAAY,EAAE;AAAA,IAClF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAa,QAAW,EAAE,KAAK,OAAO,SAAS,GAAgE;AAC7G,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAC7C,UAAM,SAAS,SAAS,EAAE,KAAK,OAAO,YAAY,SAAS,CAAC;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAa,UAAU,EAAE,IAAI,GAAmC;AAC9D,UAAM,WAAW,MAAM,KAAK,iBAAiB;AAC7C,UAAM,SAAS,YAAY,EAAE,IAAI,CAAC;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAa,QAAuB;AAAA,EAEpC;AACF;",
6
6
  "names": []
7
7
  }