@scpxl/nodejs-framework 1.0.13 โ 1.0.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -146
- package/dist/application/base-application.d.ts +3 -0
- package/dist/application/base-application.d.ts.map +1 -1
- package/dist/application/base-application.js +23 -14
- package/dist/application/base-application.js.map +2 -2
- package/dist/cluster/cluster-manager.d.ts.map +1 -1
- package/dist/cluster/cluster-manager.js +2 -1
- package/dist/cluster/cluster-manager.js.map +2 -2
- package/dist/lifecycle/exit.d.ts +10 -0
- package/dist/lifecycle/exit.d.ts.map +1 -0
- package/dist/lifecycle/exit.js +23 -0
- package/dist/lifecycle/exit.js.map +7 -0
- package/dist/lifecycle/lifecycle-manager.d.ts +13 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-manager.js +54 -0
- package/dist/lifecycle/lifecycle-manager.js.map +7 -0
- package/dist/logger/logger.d.ts +4 -1
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.js +34 -19
- package/dist/logger/logger.js.map +2 -2
- package/dist/websocket/websocket-base.js +1 -1
- package/dist/websocket/websocket-base.js.map +1 -1
- package/package.json +19 -12
package/README.md
CHANGED
|
@@ -2,194 +2,117 @@
|
|
|
2
2
|
|
|
3
3
|
A comprehensive Node.js framework for building modern applications with support for web servers, databases, queues, caching, and more.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Opinionated TypeScript framework combining Fastify, WebSockets, Redis, BullMQ, and MikroORM under a unified Application lifecycle.
|
|
6
6
|
|
|
7
|
-
Install
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
|
-
```
|
|
9
|
+
```bash
|
|
10
10
|
npm install @scpxl/nodejs-framework
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Quick Start
|
|
14
14
|
|
|
15
15
|
```ts
|
|
16
|
-
import {
|
|
16
|
+
import { Application } from '@scpxl/nodejs-framework';
|
|
17
17
|
|
|
18
|
-
const app = new
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
host: '0.0.0.0'
|
|
22
|
-
},
|
|
23
|
-
database: {
|
|
24
|
-
host: 'localhost',
|
|
25
|
-
port: 5432,
|
|
26
|
-
dbName: 'myapp',
|
|
27
|
-
user: 'user',
|
|
28
|
-
password: 'password'
|
|
29
|
-
}
|
|
18
|
+
const app = new Application({
|
|
19
|
+
webserver: { port: 3000 },
|
|
20
|
+
logger: { level: 'info' },
|
|
30
21
|
});
|
|
31
22
|
|
|
32
23
|
await app.start();
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Features
|
|
36
|
-
|
|
37
|
-
- ๐ **FastAPI-powered Web Server** with auto-routing
|
|
38
|
-
- ๐๏ธ **Database Integration** with MikroORM and PostgreSQL
|
|
39
|
-
- ๐ฆ **Queue System** using BullMQ and Redis
|
|
40
|
-
- ๐ **WebSocket Support** for real-time communication
|
|
41
|
-
- ๐๏ธ **Caching Layer** with Redis integration
|
|
42
|
-
- โ๏ธ **AWS S3 Integration** for file storage
|
|
43
|
-
- ๐ **Authentication System** with JWT
|
|
44
|
-
- ๐ **Structured Logging** with Winston
|
|
45
|
-
- ๐งช **Built-in Testing Utils**
|
|
46
|
-
- โก **Performance Monitoring**
|
|
47
24
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
```sh
|
|
55
|
-
git clone https://github.com/PXLbros/pxl-nodejs-framework.git
|
|
56
|
-
cd pxl-nodejs-framework
|
|
57
|
-
nvm use
|
|
58
|
-
npm install
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Build
|
|
62
|
-
|
|
63
|
-
```sh
|
|
64
|
-
npm run build
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### Testing with Yalc (Local Development)
|
|
68
|
-
|
|
69
|
-
For local testing before publishing to NPM:
|
|
70
|
-
|
|
71
|
-
```sh
|
|
72
|
-
# Build and publish locally
|
|
73
|
-
npm run build:local
|
|
74
|
-
|
|
75
|
-
# In your project
|
|
76
|
-
yalc add @pxl/nodejs-framework
|
|
25
|
+
app.webserver.route({
|
|
26
|
+
method: 'GET',
|
|
27
|
+
url: '/health',
|
|
28
|
+
handler: async () => ({ ok: true }),
|
|
29
|
+
});
|
|
77
30
|
```
|
|
78
31
|
|
|
79
|
-
##
|
|
80
|
-
|
|
81
|
-
### Local Development
|
|
82
|
-
|
|
83
|
-
For local development with automatic publishing through **Yalc**:
|
|
32
|
+
## Add WebSocket
|
|
84
33
|
|
|
85
|
-
```
|
|
86
|
-
|
|
34
|
+
```ts
|
|
35
|
+
app.websocket.onConnection(client => {
|
|
36
|
+
client.sendJSON({ welcome: true });
|
|
37
|
+
});
|
|
87
38
|
```
|
|
88
39
|
|
|
89
|
-
|
|
40
|
+
## Add Queue Job
|
|
90
41
|
|
|
91
|
-
```
|
|
92
|
-
|
|
42
|
+
```ts
|
|
43
|
+
await app.queue.manager.add('email', { userId: 123 });
|
|
93
44
|
```
|
|
94
45
|
|
|
95
|
-
|
|
46
|
+
## Configuration Example
|
|
96
47
|
|
|
97
|
-
```
|
|
98
|
-
|
|
48
|
+
```ts
|
|
49
|
+
new Application({
|
|
50
|
+
webserver: { port: 3000 },
|
|
51
|
+
websocket: { enabled: true },
|
|
52
|
+
queue: { enabled: true },
|
|
53
|
+
redis: { host: '127.0.0.1', port: 6379 },
|
|
54
|
+
database: {
|
|
55
|
+
/* MikroORM config */
|
|
56
|
+
},
|
|
57
|
+
logger: { level: 'info' },
|
|
58
|
+
});
|
|
99
59
|
```
|
|
100
60
|
|
|
101
|
-
##
|
|
102
|
-
|
|
103
|
-
This project uses GitHub Actions for continuous integration and deployment:
|
|
104
|
-
|
|
105
|
-
### Build and Test (CI)
|
|
106
|
-
|
|
107
|
-
- Runs on all pull requests
|
|
108
|
-
- Installs dependencies, runs linting, builds the project, and runs tests
|
|
109
|
-
|
|
110
|
-
### Build and Publish
|
|
111
|
-
|
|
112
|
-
- Runs on pushes to `main`/`master` branches and version tags
|
|
113
|
-
- Builds the project and publishes to npm
|
|
114
|
-
- Beta releases are published on pushes to main/master branches
|
|
115
|
-
- Stable releases are published when version tags are created
|
|
116
|
-
|
|
117
|
-
### Setup for Publishing
|
|
118
|
-
|
|
119
|
-
To enable automatic publishing, configure the following repository secret:
|
|
120
|
-
|
|
121
|
-
- `NPM_TOKEN`: Your npm authentication token
|
|
122
|
-
|
|
123
|
-
> **Note**: The `dist/` folder is gitignored and automatically generated during the CI/CD process. Do not commit built files to the repository.
|
|
124
|
-
|
|
125
|
-
## Releasing
|
|
61
|
+
## Features
|
|
126
62
|
|
|
127
|
-
|
|
63
|
+
- Unified lifecycle (start/stop all subsystems)
|
|
64
|
+
- Fastify routing + raw access
|
|
65
|
+
- WebSocket client + room management
|
|
66
|
+
- BullMQ queue integration
|
|
67
|
+
- MikroORM database integration
|
|
68
|
+
- Redis cache + pub/sub
|
|
69
|
+
- Structured logging
|
|
70
|
+
- Utilities & services layer
|
|
128
71
|
|
|
129
|
-
|
|
72
|
+
## Documentation
|
|
130
73
|
|
|
131
|
-
|
|
132
|
-
# Patch release (1.0.0 โ 1.0.1)
|
|
133
|
-
npm run release -- --patch
|
|
74
|
+
Full docs & guides (VitePress) + API reference (TypeDoc).
|
|
134
75
|
|
|
135
|
-
|
|
136
|
-
|
|
76
|
+
- Getting Started, Concepts, Guides
|
|
77
|
+
- API: https://pxlbros.github.io/pxl-nodejs-framework/
|
|
137
78
|
|
|
138
|
-
|
|
139
|
-
npm run release -- --major
|
|
79
|
+
To run local docs site (once cloned):
|
|
140
80
|
|
|
141
|
-
|
|
142
|
-
npm run
|
|
81
|
+
```bash
|
|
82
|
+
npm run docs:site:dev
|
|
143
83
|
```
|
|
144
84
|
|
|
145
|
-
|
|
85
|
+
## Graceful Shutdown
|
|
146
86
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
2. Verify you're on the main/master branch
|
|
151
|
-
3. Update the version in `package.json`
|
|
152
|
-
4. Create a git commit and tag
|
|
153
|
-
5. Push to origin, triggering GitHub Actions
|
|
154
|
-
|
|
155
|
-
### Dry Run
|
|
156
|
-
|
|
157
|
-
Preview what the release script will do without making changes:
|
|
158
|
-
|
|
159
|
-
```sh
|
|
160
|
-
npm run release -- --patch --dry-run
|
|
87
|
+
```ts
|
|
88
|
+
process.on('SIGINT', () => app.stop());
|
|
89
|
+
process.on('SIGTERM', () => app.stop());
|
|
161
90
|
```
|
|
162
91
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
For more information about the release script:
|
|
92
|
+
## Example Service Pattern
|
|
166
93
|
|
|
167
|
-
```
|
|
168
|
-
|
|
94
|
+
```ts
|
|
95
|
+
class UserService {
|
|
96
|
+
constructor(private app: Application) {}
|
|
97
|
+
async register(data: any) {
|
|
98
|
+
// use app.database / app.queue / app.logger
|
|
99
|
+
}
|
|
100
|
+
}
|
|
169
101
|
```
|
|
170
102
|
|
|
171
|
-
##
|
|
103
|
+
## When Not to Use
|
|
172
104
|
|
|
173
|
-
|
|
105
|
+
If you only need a single HTTP server or minimal script, this framework may be heavier than needed.
|
|
174
106
|
|
|
175
|
-
|
|
107
|
+
## Contributing
|
|
176
108
|
|
|
177
|
-
|
|
109
|
+
Issues and PRs welcome. Development scripts remain available:
|
|
178
110
|
|
|
179
|
-
```
|
|
180
|
-
npm run
|
|
111
|
+
```bash
|
|
112
|
+
npm run dev # watch build
|
|
113
|
+
npm run build # production build
|
|
181
114
|
```
|
|
182
115
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
### Automated Documentation Deployment
|
|
186
|
-
|
|
187
|
-
Documentation is automatically built and deployed to GitHub Pages when:
|
|
188
|
-
|
|
189
|
-
- Code is pushed to the `main` or `master` branch
|
|
190
|
-
- Pull requests are opened against `main` or `master` branch
|
|
191
|
-
- Manual workflow dispatch is triggered
|
|
192
|
-
|
|
193
|
-
The documentation will be available at: `https://[your-username].github.io/[repository-name]/`
|
|
116
|
+
---
|
|
194
117
|
|
|
195
|
-
|
|
118
|
+
Released under ISC License.
|
|
@@ -6,6 +6,7 @@ import type RedisInstance from '../redis/instance.js';
|
|
|
6
6
|
import CacheManager from '../cache/manager.js';
|
|
7
7
|
import EventManager from '../event/manager.js';
|
|
8
8
|
import { PerformanceMonitor } from '../performance/performance-monitor.js';
|
|
9
|
+
import { LifecycleManager } from '../lifecycle/lifecycle-manager.js';
|
|
9
10
|
export type { ApplicationConfig } from './base-application.interface.js';
|
|
10
11
|
export default abstract class BaseApplication {
|
|
11
12
|
/** Unique instance ID */
|
|
@@ -38,6 +39,8 @@ export default abstract class BaseApplication {
|
|
|
38
39
|
eventManager?: EventManager;
|
|
39
40
|
/** Performance monitor */
|
|
40
41
|
performanceMonitor?: PerformanceMonitor;
|
|
42
|
+
/** Lifecycle manager */
|
|
43
|
+
protected lifecycle: LifecycleManager;
|
|
41
44
|
get Name(): string;
|
|
42
45
|
/**
|
|
43
46
|
* Application constructor
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,gBAAgB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC9E,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAC/C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAC/C,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AAEtD,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAE/C,OAAO,YAAY,MAAM,qBAAqB,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,8 @@ import EventManager from "../event/manager.js";
|
|
|
15
15
|
import Logger from "../logger/logger.js";
|
|
16
16
|
import { PerformanceMonitor } from "../performance/performance-monitor.js";
|
|
17
17
|
import { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from "../performance/index.js";
|
|
18
|
+
import { LifecycleManager } from "../lifecycle/lifecycle-manager.js";
|
|
19
|
+
import { requestExit } from "../lifecycle/exit.js";
|
|
18
20
|
class BaseApplication {
|
|
19
21
|
static {
|
|
20
22
|
__name(this, "BaseApplication");
|
|
@@ -49,6 +51,8 @@ class BaseApplication {
|
|
|
49
51
|
eventManager;
|
|
50
52
|
/** Performance monitor */
|
|
51
53
|
performanceMonitor;
|
|
54
|
+
/** Lifecycle manager */
|
|
55
|
+
lifecycle = new LifecycleManager();
|
|
52
56
|
get Name() {
|
|
53
57
|
return this.config.name;
|
|
54
58
|
}
|
|
@@ -233,8 +237,11 @@ class BaseApplication {
|
|
|
233
237
|
await options.onStarted({ startupTime });
|
|
234
238
|
}
|
|
235
239
|
} catch (error) {
|
|
236
|
-
|
|
237
|
-
|
|
240
|
+
Logger.error({
|
|
241
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
242
|
+
message: "startInstance failure"
|
|
243
|
+
});
|
|
244
|
+
throw error;
|
|
238
245
|
}
|
|
239
246
|
}
|
|
240
247
|
/**
|
|
@@ -263,14 +270,16 @@ class BaseApplication {
|
|
|
263
270
|
if (this.config.performanceMonitoring.monitorCacheOperations !== false) {
|
|
264
271
|
CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);
|
|
265
272
|
}
|
|
266
|
-
if (this.config.performanceMonitoring
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
273
|
+
if (this.config.performanceMonitoring?.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {
|
|
274
|
+
this.lifecycle.trackInterval(
|
|
275
|
+
setInterval(() => {
|
|
276
|
+
const reportFormat = this.config.performanceMonitoring?.reportFormat ?? "detailed";
|
|
277
|
+
const report = this.performanceMonitor?.generateFormattedReport(reportFormat);
|
|
278
|
+
if (report) {
|
|
279
|
+
Logger.info({ message: report });
|
|
280
|
+
}
|
|
281
|
+
}, this.config.performanceMonitoring.reportInterval)
|
|
282
|
+
);
|
|
274
283
|
}
|
|
275
284
|
}
|
|
276
285
|
setupGlobalErrorHandlers() {
|
|
@@ -300,7 +309,7 @@ class BaseApplication {
|
|
|
300
309
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
301
310
|
message: "Error during graceful shutdown"
|
|
302
311
|
});
|
|
303
|
-
|
|
312
|
+
requestExit({ code: 1, reason: "graceful-shutdown-error", error });
|
|
304
313
|
});
|
|
305
314
|
}
|
|
306
315
|
/**
|
|
@@ -323,7 +332,7 @@ class BaseApplication {
|
|
|
323
332
|
this.isStopping = true;
|
|
324
333
|
const forceExitTimeout = setTimeout(() => {
|
|
325
334
|
Logger.warn({ message: "Forced shutdown due to timeout" });
|
|
326
|
-
|
|
335
|
+
requestExit({ code: 1, reason: "forced-timeout" });
|
|
327
336
|
}, this.shutdownTimeout);
|
|
328
337
|
try {
|
|
329
338
|
await this.stopCallback();
|
|
@@ -333,14 +342,14 @@ class BaseApplication {
|
|
|
333
342
|
await onStopped({ runtime });
|
|
334
343
|
}
|
|
335
344
|
clearTimeout(forceExitTimeout);
|
|
336
|
-
|
|
345
|
+
requestExit({ code: 0, reason: "shutdown-complete" });
|
|
337
346
|
} catch (error) {
|
|
338
347
|
Logger.error({
|
|
339
348
|
error: error instanceof Error ? error : new Error(String(error)),
|
|
340
349
|
message: "Error during shutdown"
|
|
341
350
|
});
|
|
342
351
|
clearTimeout(forceExitTimeout);
|
|
343
|
-
|
|
352
|
+
requestExit({ code: 1, reason: "shutdown-error", error });
|
|
344
353
|
}
|
|
345
354
|
}
|
|
346
355
|
}
|
|
@@ -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 { PerformanceMonitor } from '../performance/performance-monitor.js';\nimport { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from '../performance/index.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 /** Shutdown signals */\n protected shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\n /** Application start time */\n protected startTime: [number, number] = [0, 0];\n\n /** Whether application is stopping */\n protected isStopping = false;\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 public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n\n this.config = config;\n\n // const schema = Joi.object({\n // name: Joi.string().required(),\n\n // redis: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // password: Joi.string().allow('').optional(),\n // },\n\n // database: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // username: Joi.string().required(),\n // password: Joi.string().required(),\n // databaseName: Joi.string().required(),\n // },\n // });\n\n // // Validation application constructor props\n // const validationResult = schema.validate(props);\n\n // if (validationResult.error) {\n // throw new Error(validationResult.error.message);\n // }\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 applicationConfig: this.config,\n redisManager: this.redisManager,\n });\n\n // Initialize performance monitor\n this.initializePerformanceMonitor();\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 = process.hrtime();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n // onStarted: ({ startupTime }) => {\n // if (this.config.log?.startUp) {\n // Logger.info('Application started', {\n // Name: this.config.name,\n // 'PXL Framework Version': this.applicationVersion,\n // 'Startup Time': Time.formatTime({ time: startupTime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStarted) {\n // this.config.events.onStarted({ app: this, startupTime });\n // }\n // },\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\n // onStopped: ({ runtime }) => {\n // if (this.config.log?.shutdown) {\n // Logger.info('Application stopped', {\n // Name: this.config.name,\n // 'Runtime': Time.formatTime({ time: runtime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStopped) {\n // this.config.events.onStopped({ app: this, runtime });\n // }\n // },\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 // Handle standalone application shutdown\n this.handleShutdown({\n onStopped: stopInstanceOptions.onStopped,\n });\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 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 * Before application stop event\n */\n private async onBeforeStop(): Promise<void> {\n // Disconnect from Redis\n await this.redisManager.disconnect();\n\n if (this.databaseManager) {\n // Disconnect from database\n await this.databaseManager.disconnect();\n }\n }\n\n /**\n * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTime({\n startTime: this.startTime,\n });\n\n // On application started\n if (options.onStarted) {\n await options.onStarted({ startupTime });\n }\n } catch (error) {\n // Log error\n console.error(error);\n\n process.exit(1);\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 private initializePerformanceMonitor(): void {\n // Check if performance monitoring is enabled\n if (!this.config.performanceMonitoring?.enabled) {\n return;\n }\n\n // Initialize performance monitor with configuration\n this.performanceMonitor = PerformanceMonitor.initialize({\n enabled: true,\n thresholds: this.config.performanceMonitoring.thresholds,\n maxMetricsHistory: this.config.performanceMonitoring.maxMetricsHistory,\n logSlowOperations: this.config.performanceMonitoring.logSlowOperations,\n logAllOperations: this.config.performanceMonitoring.logAllOperations,\n });\n\n // Set up performance monitoring for different components\n if (this.config.performanceMonitoring.monitorDatabaseOperations !== false) {\n DatabasePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorQueueOperations !== false) {\n QueuePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorCacheOperations !== false) {\n CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n // Set up periodic performance reports if configured\n if (this.config.performanceMonitoring.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {\n setInterval(() => {\n const reportFormat = this.config.performanceMonitoring?.reportFormat ?? 'detailed';\n const report = this.performanceMonitor?.generateFormattedReport(reportFormat);\n\n if (report) {\n Logger.info({ message: report });\n }\n }, this.config.performanceMonitoring.reportInterval);\n }\n }\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 },\n });\n this.initiateGracefulShutdown();\n });\n }\n\n /**\n * Initiate graceful shutdown\n */\n private initiateGracefulShutdown(): void {\n if (this.isStopping) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n this.stop().catch(error => {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during graceful shutdown',\n });\n process.exit(1);\n });\n }\n\n /**\n * Handle shutdown\n */\n public handleShutdown({ onStopped }: { onStopped?: ({ runtime }: { runtime: number }) => void }): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => {\n // Stop application\n await this.stop({ onStopped });\n });\n });\n }\n\n /**\n * Stop application\n */\n private async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.isStopping) {\n return;\n }\n\n this.isStopping = true;\n\n // Set timeout for forced termination\n const forceExitTimeout = setTimeout(() => {\n Logger.warn({ message: 'Forced shutdown due to timeout' });\n process.exit(1);\n }, this.shutdownTimeout);\n\n try {\n // Stop callback\n await this.stopCallback();\n\n // Disconnect\n await this.onBeforeStop();\n\n if (onStopped) {\n // Calculate runtime\n const runtime = process.uptime() * 1000;\n\n // Emit stopped event\n await onStopped({ runtime });\n }\n\n clearTimeout(forceExitTimeout);\n process.exit(0);\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during shutdown',\n });\n clearTimeout(forceExitTimeout);\n process.exit(1);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;AAAA,OAAO,aAAa;AACpB,SAAS,YAAY,oBAAoB;AACzC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,MAAM,eAAe;AACvC,SAAgC,uBAAuB;AACvD,OAAO,kBAAkB;AACzB,OAAO,kBAAkB;AAMzB,OAAO,oBAAoB;AAE3B,SAAS,IAAI,YAAY;AACzB,OAAO,kBAAkB;AACzB,OAAO,QAAQ;AACf,OAAO,kBAAkB;AACzB,OAAO,YAAY;AACnB,SAAS,0BAA0B;AACnC,SAAS,yBAAyB,4BAA4B,+BAA+B;
|
|
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 { PerformanceMonitor } from '../performance/performance-monitor.js';\nimport { CachePerformanceWrapper, DatabasePerformanceWrapper, QueuePerformanceWrapper } from '../performance/index.js';\nimport { LifecycleManager } from '../lifecycle/lifecycle-manager.js';\nimport { 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 /** Shutdown signals */\n protected shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n\n /** Application start time */\n protected startTime: [number, number] = [0, 0];\n\n /** Whether application is stopping */\n protected isStopping = false;\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 protected lifecycle: LifecycleManager = new LifecycleManager();\n\n public get Name() {\n return this.config.name;\n }\n\n /**\n * Application constructor\n */\n constructor(config: ApplicationConfig) {\n const computerName = os.hostname();\n\n this.uniqueInstanceId = `${config.instanceId}-${computerName}-${OS.getUniqueComputerId()}`;\n\n this.config = config;\n\n // const schema = Joi.object({\n // name: Joi.string().required(),\n\n // redis: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // password: Joi.string().allow('').optional(),\n // },\n\n // database: {\n // host: Joi.string().required(),\n // port: Joi.number().required(),\n // username: Joi.string().required(),\n // password: Joi.string().required(),\n // databaseName: Joi.string().required(),\n // },\n // });\n\n // // Validation application constructor props\n // const validationResult = schema.validate(props);\n\n // if (validationResult.error) {\n // throw new Error(validationResult.error.message);\n // }\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 applicationConfig: this.config,\n redisManager: this.redisManager,\n });\n\n // Initialize performance monitor\n this.initializePerformanceMonitor();\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 = process.hrtime();\n\n // Get application version`\n this.applicationVersion = await this.getApplicationVersion();\n\n const startInstanceOptions: ApplicationStartInstanceOptions = {\n // onStarted: ({ startupTime }) => {\n // if (this.config.log?.startUp) {\n // Logger.info('Application started', {\n // Name: this.config.name,\n // 'PXL Framework Version': this.applicationVersion,\n // 'Startup Time': Time.formatTime({ time: startupTime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStarted) {\n // this.config.events.onStarted({ app: this, startupTime });\n // }\n // },\n onStarted: this.onStarted.bind(this),\n };\n\n const stopInstanceOptions: ApplicationStopInstanceOptions = {\n // onStopped: ({ runtime }) => {\n // if (this.config.log?.shutdown) {\n // Logger.info('Application stopped', {\n // Name: this.config.name,\n // 'Runtime': Time.formatTime({ time: runtime, format: 's', numDecimals: 2, showUnit: true }),\n // });\n // }\n\n // if (this.config.events?.onStopped) {\n // this.config.events.onStopped({ app: this, runtime });\n // }\n // },\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 // Handle standalone application shutdown\n this.handleShutdown({\n onStopped: stopInstanceOptions.onStopped,\n });\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 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 * Before application stop event\n */\n private async onBeforeStop(): Promise<void> {\n // Disconnect from Redis\n await this.redisManager.disconnect();\n\n if (this.databaseManager) {\n // Disconnect from database\n await this.databaseManager.disconnect();\n }\n }\n\n /**\n * Start application instance\n */\n private async startInstance(options: ApplicationStartInstanceOptions): Promise<void> {\n try {\n // Before application start\n const { redisInstance, databaseInstance, queueManager, eventManager } = await this.onBeforeStart();\n\n // Start application\n await this.startHandler({\n redisInstance,\n databaseInstance,\n queueManager,\n eventManager,\n });\n\n // Calculate application startup time\n const startupTime = Time.calculateElapsedTime({\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 private initializePerformanceMonitor(): void {\n // Check if performance monitoring is enabled\n if (!this.config.performanceMonitoring?.enabled) {\n return;\n }\n\n // Initialize performance monitor with configuration\n this.performanceMonitor = PerformanceMonitor.initialize({\n enabled: true,\n thresholds: this.config.performanceMonitoring.thresholds,\n maxMetricsHistory: this.config.performanceMonitoring.maxMetricsHistory,\n logSlowOperations: this.config.performanceMonitoring.logSlowOperations,\n logAllOperations: this.config.performanceMonitoring.logAllOperations,\n });\n\n // Set up performance monitoring for different components\n if (this.config.performanceMonitoring.monitorDatabaseOperations !== false) {\n DatabasePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorQueueOperations !== false) {\n QueuePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n if (this.config.performanceMonitoring.monitorCacheOperations !== false) {\n CachePerformanceWrapper.setPerformanceMonitor(this.performanceMonitor);\n }\n\n // Set up periodic performance reports if configured\n if (this.config.performanceMonitoring?.reportInterval && this.config.performanceMonitoring.reportInterval > 0) {\n this.lifecycle.trackInterval(\n setInterval(() => {\n const reportFormat = this.config.performanceMonitoring?.reportFormat ?? 'detailed';\n const report = this.performanceMonitor?.generateFormattedReport(reportFormat);\n\n if (report) {\n Logger.info({ message: report });\n }\n }, this.config.performanceMonitoring.reportInterval),\n );\n }\n }\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 },\n });\n this.initiateGracefulShutdown();\n });\n }\n\n /**\n * Initiate graceful shutdown\n */\n private initiateGracefulShutdown(): void {\n if (this.isStopping) {\n return;\n }\n\n Logger.info({ message: 'Initiating graceful shutdown due to error' });\n this.stop().catch(error => {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during graceful shutdown',\n });\n requestExit({ code: 1, reason: 'graceful-shutdown-error', error });\n });\n }\n\n /**\n * Handle shutdown\n */\n public handleShutdown({ onStopped }: { onStopped?: ({ runtime }: { runtime: number }) => void }): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => {\n // Stop application\n await this.stop({ onStopped });\n });\n });\n }\n\n /**\n * Stop application\n */\n private async stop({ onStopped }: ApplicationStopInstanceOptions = {}): Promise<void> {\n if (this.isStopping) {\n return;\n }\n\n this.isStopping = true;\n\n // Set timeout for forced termination\n const forceExitTimeout = setTimeout(() => {\n Logger.warn({ message: 'Forced shutdown due to timeout' });\n requestExit({ code: 1, reason: 'forced-timeout' });\n }, this.shutdownTimeout);\n\n try {\n // Stop callback\n await this.stopCallback();\n\n // Disconnect\n await this.onBeforeStop();\n\n if (onStopped) {\n // Calculate runtime\n const runtime = process.uptime() * 1000;\n // Emit stopped event\n await onStopped({ runtime });\n }\n\n clearTimeout(forceExitTimeout);\n requestExit({ code: 0, reason: 'shutdown-complete' });\n } catch (error) {\n Logger.error({\n error: error instanceof Error ? error : new Error(String(error)),\n message: 'Error during shutdown',\n });\n clearTimeout(forceExitTimeout);\n requestExit({ code: 1, reason: 'shutdown-error', error });\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cluster-manager.d.ts","sourceRoot":"","sources":["../../src/cluster/cluster-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"cluster-manager.d.ts","sourceRoot":"","sources":["../../src/cluster/cluster-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,mBAAmB,EAEpB,MAAM,gCAAgC,CAAC;AAIxC,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuB;IAE9C,OAAO,CAAC,wBAAwB,CAAsB;IACtD,OAAO,CAAC,uBAAuB,CAAsB;IAErD,OAAO,CAAC,eAAe,CAA2C;IAClE,OAAO,CAAC,cAAc,CAAS;gBAEnB,EAAE,MAAM,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,EAAE,mBAAmB;IAOvF,KAAK,IAAI,IAAI;IAUpB,OAAO,CAAC,YAAY;YAqCN,WAAW;IAkBzB,OAAO,CAAC,cAAc;YAMR,gBAAgB;CAkC/B"}
|
|
@@ -3,6 +3,7 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
3
3
|
import cluster from "node:cluster";
|
|
4
4
|
import { cpus } from "node:os";
|
|
5
5
|
import { Logger } from "../logger/index.js";
|
|
6
|
+
import { requestExit } from "../lifecycle/exit.js";
|
|
6
7
|
class ClusterManager {
|
|
7
8
|
static {
|
|
8
9
|
__name(this, "ClusterManager");
|
|
@@ -92,7 +93,7 @@ class ClusterManager {
|
|
|
92
93
|
const clusterWorkers = cluster.workers ?? {};
|
|
93
94
|
const numClusterWorkers = Object.keys(clusterWorkers).length;
|
|
94
95
|
if (exitedWorkers === numClusterWorkers) {
|
|
95
|
-
|
|
96
|
+
requestExit({ code: 0, reason: "cluster-workers-exited" });
|
|
96
97
|
}
|
|
97
98
|
});
|
|
98
99
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/cluster/cluster-manager.ts"],
|
|
4
|
-
"sourcesContent": ["import cluster from 'node:cluster';\nimport { cpus } from 'node:os';\nimport type {\n ClusterManagerConfig,\n ClusterManagerProps,\n ClusterManagerWorkerModeManualConfig,\n} from './cluster-manager.interface.js';\nimport { Logger } from '../logger/index.js';\n\nexport default class ClusterManager {\n private readonly config: ClusterManagerConfig;\n\n private startApplicationCallback: () => Promise<void>;\n private stopApplicationCallback: () => Promise<void>;\n\n private shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n private isShuttingDown = false;\n\n constructor({ config, startApplicationCallback, stopApplicationCallback }: ClusterManagerProps) {\n this.config = config;\n\n this.startApplicationCallback = startApplicationCallback;\n this.stopApplicationCallback = stopApplicationCallback;\n }\n\n public start(): void {\n if (cluster.isPrimary) {\n this.setupPrimary();\n } else {\n this.setupWorker();\n }\n\n this.handleShutdown();\n }\n\n private setupPrimary(): void {\n const numCPUs: number = cpus().length;\n\n const numClusterWorkers =\n this.config.workerMode === 'auto' ? numCPUs : (this.config as ClusterManagerWorkerModeManualConfig).workerCount;\n\n for (let workerIndex = 0; workerIndex < numClusterWorkers; workerIndex++) {\n cluster.fork();\n }\n\n cluster.on('online', worker => {\n Logger.debug({\n message: 'Started cluster worker',\n meta: {\n ID: worker.id,\n PID: worker.process.pid,\n },\n });\n });\n\n cluster.on('exit', () => {\n if (!this.isShuttingDown) {\n // Restart worker on unexpected exit\n cluster.fork();\n }\n });\n\n Logger.debug({\n message: 'Started cluster master',\n meta: {\n Mode: this.config.workerMode,\n 'Worker Count': numClusterWorkers,\n CPUs: numCPUs,\n },\n });\n }\n\n private async setupWorker(): Promise<void> {\n await this.startApplicationCallback();\n\n process.on('message', async message => {\n if (message === 'shutdown') {\n Logger.debug({\n message: 'Worker received shutdown message, stopping...',\n meta: {\n PID: process.pid,\n },\n });\n\n // Stop application\n await this.stopApplicationCallback();\n }\n });\n }\n\n private handleShutdown(): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => await this.initiateShutdown());\n });\n }\n\n private async initiateShutdown(): Promise<void> {\n if (this.isShuttingDown) {\n return;\n }\n\n this.isShuttingDown = true;\n\n if (cluster.isPrimary) {\n for (const id in cluster.workers) {\n const worker = cluster.workers[id];\n\n if (!worker) {\n continue;\n }\n\n worker.send('shutdown');\n }\n\n let exitedWorkers = 0;\n\n cluster.on('exit', () => {\n exitedWorkers++;\n\n const clusterWorkers = cluster.workers ?? {};\n const numClusterWorkers = Object.keys(clusterWorkers).length;\n\n if (exitedWorkers === numClusterWorkers) {\n
|
|
5
|
-
"mappings": ";;AAAA,OAAO,aAAa;AACpB,SAAS,YAAY;AAMrB,SAAS,cAAc;
|
|
4
|
+
"sourcesContent": ["import cluster from 'node:cluster';\nimport { cpus } from 'node:os';\nimport type {\n ClusterManagerConfig,\n ClusterManagerProps,\n ClusterManagerWorkerModeManualConfig,\n} from './cluster-manager.interface.js';\nimport { Logger } from '../logger/index.js';\nimport { requestExit } from '../lifecycle/exit.js';\n\nexport default class ClusterManager {\n private readonly config: ClusterManagerConfig;\n\n private startApplicationCallback: () => Promise<void>;\n private stopApplicationCallback: () => Promise<void>;\n\n private shutdownSignals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT'];\n private isShuttingDown = false;\n\n constructor({ config, startApplicationCallback, stopApplicationCallback }: ClusterManagerProps) {\n this.config = config;\n\n this.startApplicationCallback = startApplicationCallback;\n this.stopApplicationCallback = stopApplicationCallback;\n }\n\n public start(): void {\n if (cluster.isPrimary) {\n this.setupPrimary();\n } else {\n this.setupWorker();\n }\n\n this.handleShutdown();\n }\n\n private setupPrimary(): void {\n const numCPUs: number = cpus().length;\n\n const numClusterWorkers =\n this.config.workerMode === 'auto' ? numCPUs : (this.config as ClusterManagerWorkerModeManualConfig).workerCount;\n\n for (let workerIndex = 0; workerIndex < numClusterWorkers; workerIndex++) {\n cluster.fork();\n }\n\n cluster.on('online', worker => {\n Logger.debug({\n message: 'Started cluster worker',\n meta: {\n ID: worker.id,\n PID: worker.process.pid,\n },\n });\n });\n\n cluster.on('exit', () => {\n if (!this.isShuttingDown) {\n // Restart worker on unexpected exit\n cluster.fork();\n }\n });\n\n Logger.debug({\n message: 'Started cluster master',\n meta: {\n Mode: this.config.workerMode,\n 'Worker Count': numClusterWorkers,\n CPUs: numCPUs,\n },\n });\n }\n\n private async setupWorker(): Promise<void> {\n await this.startApplicationCallback();\n\n process.on('message', async message => {\n if (message === 'shutdown') {\n Logger.debug({\n message: 'Worker received shutdown message, stopping...',\n meta: {\n PID: process.pid,\n },\n });\n\n // Stop application\n await this.stopApplicationCallback();\n }\n });\n }\n\n private handleShutdown(): void {\n this.shutdownSignals.forEach(signal => {\n process.on(signal, async () => await this.initiateShutdown());\n });\n }\n\n private async initiateShutdown(): Promise<void> {\n if (this.isShuttingDown) {\n return;\n }\n\n this.isShuttingDown = true;\n\n if (cluster.isPrimary) {\n for (const id in cluster.workers) {\n const worker = cluster.workers[id];\n\n if (!worker) {\n continue;\n }\n\n worker.send('shutdown');\n }\n\n let exitedWorkers = 0;\n\n cluster.on('exit', () => {\n exitedWorkers++;\n\n const clusterWorkers = cluster.workers ?? {};\n const numClusterWorkers = Object.keys(clusterWorkers).length;\n\n if (exitedWorkers === numClusterWorkers) {\n requestExit({ code: 0, reason: 'cluster-workers-exited' });\n }\n });\n } else {\n await this.stopApplicationCallback();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAAA,OAAO,aAAa;AACpB,SAAS,YAAY;AAMrB,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAE5B,MAAO,eAA6B;AAAA,EAVpC,OAUoC;AAAA;AAAA;AAAA,EACjB;AAAA,EAET;AAAA,EACA;AAAA,EAEA,kBAAoC,CAAC,WAAW,QAAQ;AAAA,EACxD,iBAAiB;AAAA,EAEzB,YAAY,EAAE,QAAQ,0BAA0B,wBAAwB,GAAwB;AAC9F,SAAK,SAAS;AAEd,SAAK,2BAA2B;AAChC,SAAK,0BAA0B;AAAA,EACjC;AAAA,EAEO,QAAc;AACnB,QAAI,QAAQ,WAAW;AACrB,WAAK,aAAa;AAAA,IACpB,OAAO;AACL,WAAK,YAAY;AAAA,IACnB;AAEA,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,eAAqB;AAC3B,UAAM,UAAkB,KAAK,EAAE;AAE/B,UAAM,oBACJ,KAAK,OAAO,eAAe,SAAS,UAAW,KAAK,OAAgD;AAEtG,aAAS,cAAc,GAAG,cAAc,mBAAmB,eAAe;AACxE,cAAQ,KAAK;AAAA,IACf;AAEA,YAAQ,GAAG,UAAU,YAAU;AAC7B,aAAO,MAAM;AAAA,QACX,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,IAAI,OAAO;AAAA,UACX,KAAK,OAAO,QAAQ;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,YAAQ,GAAG,QAAQ,MAAM;AACvB,UAAI,CAAC,KAAK,gBAAgB;AAExB,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AAAA,MACX,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,MAAM,KAAK,OAAO;AAAA,QAClB,gBAAgB;AAAA,QAChB,MAAM;AAAA,MACR;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,cAA6B;AACzC,UAAM,KAAK,yBAAyB;AAEpC,YAAQ,GAAG,WAAW,OAAM,YAAW;AACrC,UAAI,YAAY,YAAY;AAC1B,eAAO,MAAM;AAAA,UACX,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,KAAK,QAAQ;AAAA,UACf;AAAA,QACF,CAAC;AAGD,cAAM,KAAK,wBAAwB;AAAA,MACrC;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEQ,iBAAuB;AAC7B,SAAK,gBAAgB,QAAQ,YAAU;AACrC,cAAQ,GAAG,QAAQ,YAAY,MAAM,KAAK,iBAAiB,CAAC;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA,EAEA,MAAc,mBAAkC;AAC9C,QAAI,KAAK,gBAAgB;AACvB;AAAA,IACF;AAEA,SAAK,iBAAiB;AAEtB,QAAI,QAAQ,WAAW;AACrB,iBAAW,MAAM,QAAQ,SAAS;AAChC,cAAM,SAAS,QAAQ,QAAQ,EAAE;AAEjC,YAAI,CAAC,QAAQ;AACX;AAAA,QACF;AAEA,eAAO,KAAK,UAAU;AAAA,MACxB;AAEA,UAAI,gBAAgB;AAEpB,cAAQ,GAAG,QAAQ,MAAM;AACvB;AAEA,cAAM,iBAAiB,QAAQ,WAAW,CAAC;AAC3C,cAAM,oBAAoB,OAAO,KAAK,cAAc,EAAE;AAEtD,YAAI,kBAAkB,mBAAmB;AACvC,sBAAY,EAAE,MAAM,GAAG,QAAQ,yBAAyB,CAAC;AAAA,QAC3D;AAAA,MACF,CAAC;AAAA,IACH,OAAO;AACL,YAAM,KAAK,wBAAwB;AAAA,IACrC;AAAA,EACF;AACF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ExitOutcome {
|
|
2
|
+
code: number;
|
|
3
|
+
reason: string;
|
|
4
|
+
error?: unknown;
|
|
5
|
+
}
|
|
6
|
+
type ExitHandler = (outcome: ExitOutcome) => void;
|
|
7
|
+
export declare function setExitHandler(next: ExitHandler): void;
|
|
8
|
+
export declare function requestExit(outcome: ExitOutcome): void;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=exit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exit.d.ts","sourceRoot":"","sources":["../../src/lifecycle/exit.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,KAAK,WAAW,GAAG,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;AAMlD,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,QAE/C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,QAO/C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
let handler = /* @__PURE__ */ __name((outcome) => {
|
|
4
|
+
process.exit(outcome.code);
|
|
5
|
+
}, "handler");
|
|
6
|
+
function setExitHandler(next) {
|
|
7
|
+
handler = next;
|
|
8
|
+
}
|
|
9
|
+
__name(setExitHandler, "setExitHandler");
|
|
10
|
+
function requestExit(outcome) {
|
|
11
|
+
try {
|
|
12
|
+
handler(outcome);
|
|
13
|
+
} catch (err) {
|
|
14
|
+
console.error("Exit handler failure", err);
|
|
15
|
+
process.exit(outcome.code);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
__name(requestExit, "requestExit");
|
|
19
|
+
export {
|
|
20
|
+
requestExit,
|
|
21
|
+
setExitHandler
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=exit.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lifecycle/exit.ts"],
|
|
4
|
+
"sourcesContent": ["export interface ExitOutcome {\n code: number;\n reason: string;\n error?: unknown;\n}\n\ntype ExitHandler = (outcome: ExitOutcome) => void;\n\nlet handler: ExitHandler = outcome => {\n process.exit(outcome.code);\n};\n\nexport function setExitHandler(next: ExitHandler) {\n handler = next;\n}\n\nexport function requestExit(outcome: ExitOutcome) {\n try {\n handler(outcome);\n } catch (err) {\n console.error('Exit handler failure', err);\n process.exit(outcome.code);\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAQA,IAAI,UAAuB,oCAAW;AACpC,UAAQ,KAAK,QAAQ,IAAI;AAC3B,GAF2B;AAIpB,SAAS,eAAe,MAAmB;AAChD,YAAU;AACZ;AAFgB;AAIT,SAAS,YAAY,SAAsB;AAChD,MAAI;AACF,YAAQ,OAAO;AAAA,EACjB,SAAS,KAAK;AACZ,YAAQ,MAAM,wBAAwB,GAAG;AACzC,YAAQ,KAAK,QAAQ,IAAI;AAAA,EAC3B;AACF;AAPgB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare class LifecycleManager {
|
|
2
|
+
private shutdownHooks;
|
|
3
|
+
private intervals;
|
|
4
|
+
private timeouts;
|
|
5
|
+
private shuttingDown;
|
|
6
|
+
onShutdown(fn: () => void | Promise<void>): () => void;
|
|
7
|
+
trackInterval(id: NodeJS.Timeout): NodeJS.Timeout;
|
|
8
|
+
trackTimeout(id: NodeJS.Timeout): NodeJS.Timeout;
|
|
9
|
+
shutdown(): Promise<{
|
|
10
|
+
errors: unknown[];
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=lifecycle-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lifecycle-manager.d.ts","sourceRoot":"","sources":["../../src/lifecycle/lifecycle-manager.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAAyC;IAC9D,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,QAAQ,CAA6B;IAC7C,OAAO,CAAC,YAAY,CAAS;IAE7B,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzC,aAAa,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO;IAKhC,YAAY,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO;IAKzB,QAAQ,IAAI,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,EAAE,CAAA;KAAE,CAAC;CA0BjD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
class LifecycleManager {
|
|
4
|
+
static {
|
|
5
|
+
__name(this, "LifecycleManager");
|
|
6
|
+
}
|
|
7
|
+
shutdownHooks = [];
|
|
8
|
+
intervals = /* @__PURE__ */ new Set();
|
|
9
|
+
timeouts = /* @__PURE__ */ new Set();
|
|
10
|
+
shuttingDown = false;
|
|
11
|
+
onShutdown(fn) {
|
|
12
|
+
this.shutdownHooks.push(fn);
|
|
13
|
+
return () => {
|
|
14
|
+
const i = this.shutdownHooks.indexOf(fn);
|
|
15
|
+
if (i >= 0) this.shutdownHooks.splice(i, 1);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
trackInterval(id) {
|
|
19
|
+
this.intervals.add(id);
|
|
20
|
+
return id;
|
|
21
|
+
}
|
|
22
|
+
trackTimeout(id) {
|
|
23
|
+
this.timeouts.add(id);
|
|
24
|
+
return id;
|
|
25
|
+
}
|
|
26
|
+
async shutdown() {
|
|
27
|
+
if (this.shuttingDown) {
|
|
28
|
+
return { errors: [] };
|
|
29
|
+
}
|
|
30
|
+
this.shuttingDown = true;
|
|
31
|
+
for (const id of this.intervals) {
|
|
32
|
+
clearInterval(id);
|
|
33
|
+
}
|
|
34
|
+
for (const id of this.timeouts) {
|
|
35
|
+
clearTimeout(id);
|
|
36
|
+
}
|
|
37
|
+
this.intervals.clear();
|
|
38
|
+
this.timeouts.clear();
|
|
39
|
+
const errors = [];
|
|
40
|
+
for (let i = this.shutdownHooks.length - 1; i >= 0; i--) {
|
|
41
|
+
const hook = this.shutdownHooks[i];
|
|
42
|
+
try {
|
|
43
|
+
await hook();
|
|
44
|
+
} catch (e) {
|
|
45
|
+
errors.push(e);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { errors };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
LifecycleManager
|
|
53
|
+
};
|
|
54
|
+
//# sourceMappingURL=lifecycle-manager.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/lifecycle/lifecycle-manager.ts"],
|
|
4
|
+
"sourcesContent": ["export class LifecycleManager {\n private shutdownHooks: Array<() => void | Promise<void>> = [];\n private intervals = new Set<NodeJS.Timeout>();\n private timeouts = new Set<NodeJS.Timeout>();\n private shuttingDown = false;\n\n onShutdown(fn: () => void | Promise<void>) {\n this.shutdownHooks.push(fn);\n return () => {\n const i = this.shutdownHooks.indexOf(fn);\n if (i >= 0) this.shutdownHooks.splice(i, 1);\n };\n }\n\n trackInterval(id: NodeJS.Timeout) {\n this.intervals.add(id);\n return id;\n }\n\n trackTimeout(id: NodeJS.Timeout) {\n this.timeouts.add(id);\n return id;\n }\n\n async shutdown(): Promise<{ errors: unknown[] }> {\n if (this.shuttingDown) {\n return { errors: [] };\n }\n this.shuttingDown = true;\n\n for (const id of this.intervals) {\n clearInterval(id);\n }\n for (const id of this.timeouts) {\n clearTimeout(id);\n }\n this.intervals.clear();\n this.timeouts.clear();\n\n const errors: unknown[] = [];\n for (let i = this.shutdownHooks.length - 1; i >= 0; i--) {\n const hook = this.shutdownHooks[i];\n try {\n await hook();\n } catch (e) {\n errors.push(e);\n }\n }\n return { errors };\n }\n}\n"],
|
|
5
|
+
"mappings": ";;AAAO,MAAM,iBAAiB;AAAA,EAA9B,OAA8B;AAAA;AAAA;AAAA,EACpB,gBAAmD,CAAC;AAAA,EACpD,YAAY,oBAAI,IAAoB;AAAA,EACpC,WAAW,oBAAI,IAAoB;AAAA,EACnC,eAAe;AAAA,EAEvB,WAAW,IAAgC;AACzC,SAAK,cAAc,KAAK,EAAE;AAC1B,WAAO,MAAM;AACX,YAAM,IAAI,KAAK,cAAc,QAAQ,EAAE;AACvC,UAAI,KAAK,EAAG,MAAK,cAAc,OAAO,GAAG,CAAC;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,cAAc,IAAoB;AAChC,SAAK,UAAU,IAAI,EAAE;AACrB,WAAO;AAAA,EACT;AAAA,EAEA,aAAa,IAAoB;AAC/B,SAAK,SAAS,IAAI,EAAE;AACpB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAA2C;AAC/C,QAAI,KAAK,cAAc;AACrB,aAAO,EAAE,QAAQ,CAAC,EAAE;AAAA,IACtB;AACA,SAAK,eAAe;AAEpB,eAAW,MAAM,KAAK,WAAW;AAC/B,oBAAc,EAAE;AAAA,IAClB;AACA,eAAW,MAAM,KAAK,UAAU;AAC9B,mBAAa,EAAE;AAAA,IACjB;AACA,SAAK,UAAU,MAAM;AACrB,SAAK,SAAS,MAAM;AAEpB,UAAM,SAAoB,CAAC;AAC3B,aAAS,IAAI,KAAK,cAAc,SAAS,GAAG,KAAK,GAAG,KAAK;AACvD,YAAM,OAAO,KAAK,cAAc,CAAC;AACjC,UAAI;AACF,cAAM,KAAK;AAAA,MACb,SAAS,GAAG;AACV,eAAO,KAAK,CAAC;AAAA,MACf;AAAA,IACF;AACA,WAAO,EAAE,OAAO;AAAA,EAClB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/logger/logger.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export declare class Logger {
|
|
|
12
12
|
sentryDsn: string;
|
|
13
13
|
environment: string;
|
|
14
14
|
}): void;
|
|
15
|
-
log({ level, message, meta, options, }: {
|
|
15
|
+
log({ level, message, meta, options: _options, }: {
|
|
16
16
|
level: LoggerLevels;
|
|
17
17
|
message: unknown;
|
|
18
18
|
meta?: Record<string, unknown>;
|
|
@@ -23,16 +23,19 @@ export declare class Logger {
|
|
|
23
23
|
meta?: Record<string, unknown>;
|
|
24
24
|
options?: LogOptions;
|
|
25
25
|
}): void;
|
|
26
|
+
debug(message: unknown, meta?: Record<string, unknown>): void;
|
|
26
27
|
info({ message, meta, options, }: {
|
|
27
28
|
message: unknown;
|
|
28
29
|
meta?: Record<string, unknown>;
|
|
29
30
|
options?: LogOptions;
|
|
30
31
|
}): void;
|
|
32
|
+
info(message: unknown, meta?: Record<string, unknown>): void;
|
|
31
33
|
warn({ message, meta, options, }: {
|
|
32
34
|
message: unknown;
|
|
33
35
|
meta?: Record<string, unknown>;
|
|
34
36
|
options?: LogOptions;
|
|
35
37
|
}): void;
|
|
38
|
+
warn(message: unknown, meta?: Record<string, unknown>): void;
|
|
36
39
|
error({ error, message, meta, options, }: {
|
|
37
40
|
error: Error | unknown;
|
|
38
41
|
message?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger/logger.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,GACT,UAAU,GACV,OAAO,GACP,WAAW,GACX,WAAW,GACX,OAAO,GACP,UAAU,GACV,OAAO,GACP,OAAO,CAAC;AAEZ,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAS;IAChC,OAAO,CAAC,MAAM,CAAiB;IAE/B,OAAO,CAAC,WAAW,CAAqB;IAEjC,mBAAmB,UAAS;IAEnC,OAAO;WAwDO,WAAW,IAAI,MAAM;IAQnC,OAAO,CAAC,eAAe;IAwBhB,UAAU,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAiBxF,GAAG,CAAC,EACT,KAAK,EACL,OAAO,EACP,IAAI,EACJ,OAAO,
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger/logger.ts"],"names":[],"mappings":"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"}
|
package/dist/logger/logger.js
CHANGED
|
@@ -102,7 +102,7 @@ class Logger {
|
|
|
102
102
|
level,
|
|
103
103
|
message,
|
|
104
104
|
meta,
|
|
105
|
-
options
|
|
105
|
+
options: _options
|
|
106
106
|
}) {
|
|
107
107
|
if (message instanceof Error) {
|
|
108
108
|
const errorMessage = message.stack ?? message.toString();
|
|
@@ -113,26 +113,41 @@ class Logger {
|
|
|
113
113
|
this.logger.log(level, JSON.stringify(message), meta);
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
|
-
debug({
|
|
117
|
-
message
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
debug(messageOrOptions, meta) {
|
|
117
|
+
if (typeof messageOrOptions === "object" && messageOrOptions !== null && "message" in messageOrOptions) {
|
|
118
|
+
const {
|
|
119
|
+
message,
|
|
120
|
+
meta: optionsMeta,
|
|
121
|
+
options
|
|
122
|
+
} = messageOrOptions;
|
|
123
|
+
this.log({ level: "debug", message, meta: optionsMeta, options });
|
|
124
|
+
} else {
|
|
125
|
+
this.log({ level: "debug", message: messageOrOptions, meta, options: void 0 });
|
|
126
|
+
}
|
|
122
127
|
}
|
|
123
|
-
info({
|
|
124
|
-
message
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
128
|
+
info(messageOrOptions, meta) {
|
|
129
|
+
if (typeof messageOrOptions === "object" && messageOrOptions !== null && "message" in messageOrOptions) {
|
|
130
|
+
const {
|
|
131
|
+
message,
|
|
132
|
+
meta: optionsMeta,
|
|
133
|
+
options
|
|
134
|
+
} = messageOrOptions;
|
|
135
|
+
this.log({ level: "info", message, meta: optionsMeta, options });
|
|
136
|
+
} else {
|
|
137
|
+
this.log({ level: "info", message: messageOrOptions, meta, options: void 0 });
|
|
138
|
+
}
|
|
129
139
|
}
|
|
130
|
-
warn({
|
|
131
|
-
message
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
140
|
+
warn(messageOrOptions, meta) {
|
|
141
|
+
if (typeof messageOrOptions === "object" && messageOrOptions !== null && "message" in messageOrOptions) {
|
|
142
|
+
const {
|
|
143
|
+
message,
|
|
144
|
+
meta: optionsMeta,
|
|
145
|
+
options
|
|
146
|
+
} = messageOrOptions;
|
|
147
|
+
this.log({ level: "warn", message, meta: optionsMeta, options });
|
|
148
|
+
} else {
|
|
149
|
+
this.log({ level: "warn", message: messageOrOptions, meta, options: void 0 });
|
|
150
|
+
}
|
|
136
151
|
}
|
|
137
152
|
error({
|
|
138
153
|
error,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/logger/logger.ts"],
|
|
4
|
-
"sourcesContent": ["import * as Sentry from '@sentry/node';\nimport { nodeProfilingIntegration } from '@sentry/profiling-node';\nimport cluster from 'node:cluster';\nimport winston from 'winston';\nimport type { LogOptions } from '../websocket/utils.js';\n\nexport type LoggerLevels =\n | 'error'\n | 'warn'\n | 'info'\n | 'command'\n | 'database'\n | 'redis'\n | 'webServer'\n | 'webSocket'\n | 'queue'\n | 'queueJob'\n | 'event'\n | 'debug';\n\nexport class Logger {\n private static instance: Logger;\n private logger: winston.Logger;\n\n private environment: string | undefined;\n\n public isSentryInitialized = false;\n\n private constructor() {\n this.environment = process.env.NODE_ENV;\n\n const customFormat = this.getCustomFormat();\n\n const customLevels: winston.config.AbstractConfigSetLevels = {\n error: 0,\n warn: 1,\n info: 2,\n command: 3,\n database: 4,\n redis: 5,\n webServer: 6,\n webSocket: 7,\n queue: 8,\n queueJob: 9,\n event: 10,\n debug: 11,\n };\n\n const customColors: winston.config.AbstractConfigSetColors = {\n error: 'red',\n warn: 'yellow',\n info: 'blue',\n command: 'cyan',\n database: 'brightGreen',\n redis: 'brightYellow',\n webServer: 'brightBlue',\n webSocket: 'brightMagenta',\n queue: 'gray',\n queueJob: 'blue',\n event: 'brightGreen',\n debug: 'brightCyan',\n };\n\n winston.addColors(customColors);\n\n this.logger = winston.createLogger({\n levels: customLevels,\n level: this.environment === 'production' ? 'info' : 'debug',\n format: winston.format.combine(\n winston.format.timestamp({\n format: 'YYYY-MM-DD HH:mm:ss',\n }),\n winston.format.errors({ stack: true }),\n winston.format.splat(),\n winston.format.json(),\n ),\n transports: [\n new winston.transports.Console({\n format: winston.format.combine(winston.format.colorize(), customFormat),\n }),\n ],\n });\n }\n\n public static getInstance(): Logger {\n if (!Logger.instance) {\n Logger.instance = new Logger();\n }\n\n return Logger.instance;\n }\n\n private getCustomFormat(): winston.Logform.Format {\n return winston.format.printf(({ level, message, timestamp, ...meta }) => {\n if (cluster.isWorker && cluster.worker) {\n meta['Worker'] = cluster.worker.id; // .process.pid;\n }\n\n const metaString = Object.entries(meta)\n .map(([key, value]) => {\n return `${key}: ${value}`;\n })\n .join(' | ');\n\n if (level === 'error') {\n if (this.isSentryInitialized) {\n const errorMessage = typeof message === 'string' ? message : JSON.stringify(message);\n\n Sentry.captureException(new Error(errorMessage));\n }\n }\n\n return `[${timestamp}] ${level}: ${message}${metaString ? ` (${metaString})` : ''}`;\n });\n }\n\n public initSentry({ sentryDsn, environment }: { sentryDsn: string; environment: string }): void {\n if (!sentryDsn) {\n this.logger.warn('Missing Sentry DSN when initializing Sentry');\n\n return;\n }\n\n Sentry.init({\n dsn: sentryDsn,\n integrations: [nodeProfilingIntegration()],\n tracesSampleRate: 1.0,\n environment,\n });\n\n this.isSentryInitialized = true;\n }\n\n public log({\n level,\n message,\n meta,\n options,\n }: {\n level: LoggerLevels;\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n // if (options?.muteWorker) {\n // }\n\n if (message instanceof Error) {\n const errorMessage = message.stack ?? message.toString();\n this.logger.log(level, errorMessage, meta);\n } else if (typeof message === 'string') {\n this.logger.log(level, message, meta);\n } else {\n this.logger.log(level, JSON.stringify(message), meta);\n }\n }\n\n public debug({\n message,\n meta,\n options,\n }: {\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n this.log({ level: 'debug', message, meta, options });\n }\n\n public info({\n message,\n meta,\n options,\n }: {\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n this.log({ level: 'info', message, meta, options });\n }\n\n public warn({\n message,\n meta,\n options,\n }: {\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n this.log({ level: 'warn', message, meta, options });\n }\n\n public error({\n error,\n message,\n meta,\n options,\n }: {\n error: Error | unknown;\n message?: string;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n if (message) {\n // If a message is provided, combine it with the error for better context\n const errorMessage = error instanceof Error ? error.message : String(error);\n const combinedMessage = `${message}: ${errorMessage}`;\n this.log({ level: 'error', message: combinedMessage, meta, options });\n\n // Also capture the original error for Sentry if it's an Error object\n if (error instanceof Error && this.isSentryInitialized) {\n Sentry.captureException(error);\n }\n } else {\n // Original behavior when no message is provided\n this.log({ level: 'error', message: error, meta, options });\n }\n }\n\n public custom({\n level,\n message,\n meta,\n options,\n }: {\n level: LoggerLevels;\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n this.log({ level, message, meta, options });\n }\n}\n\nexport default Logger.getInstance();\n"],
|
|
5
|
-
"mappings": ";;AAAA,YAAY,YAAY;AACxB,SAAS,gCAAgC;AACzC,OAAO,aAAa;AACpB,OAAO,aAAa;AAiBb,MAAM,OAAO;AAAA,EApBpB,OAoBoB;AAAA;AAAA;AAAA,EAClB,OAAe;AAAA,EACP;AAAA,EAEA;AAAA,EAED,sBAAsB;AAAA,EAErB,cAAc;AACpB,SAAK,cAAc,QAAQ,IAAI;AAE/B,UAAM,eAAe,KAAK,gBAAgB;AAE1C,UAAM,eAAuD;AAAA,MAC3D,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,MACX,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAEA,UAAM,eAAuD;AAAA,MAC3D,OAAO;AAAA,MACP,MAAM;AAAA,MACN,MAAM;AAAA,MACN,SAAS;AAAA,MACT,UAAU;AAAA,MACV,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,MACX,OAAO;AAAA,MACP,UAAU;AAAA,MACV,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AAEA,YAAQ,UAAU,YAAY;AAE9B,SAAK,SAAS,QAAQ,aAAa;AAAA,MACjC,QAAQ;AAAA,MACR,OAAO,KAAK,gBAAgB,eAAe,SAAS;AAAA,MACpD,QAAQ,QAAQ,OAAO;AAAA,QACrB,QAAQ,OAAO,UAAU;AAAA,UACvB,QAAQ;AAAA,QACV,CAAC;AAAA,QACD,QAAQ,OAAO,OAAO,EAAE,OAAO,KAAK,CAAC;AAAA,QACrC,QAAQ,OAAO,MAAM;AAAA,QACrB,QAAQ,OAAO,KAAK;AAAA,MACtB;AAAA,MACA,YAAY;AAAA,QACV,IAAI,QAAQ,WAAW,QAAQ;AAAA,UAC7B,QAAQ,QAAQ,OAAO,QAAQ,QAAQ,OAAO,SAAS,GAAG,YAAY;AAAA,QACxE,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,OAAc,cAAsB;AAClC,QAAI,CAAC,OAAO,UAAU;AACpB,aAAO,WAAW,IAAI,OAAO;AAAA,IAC/B;AAEA,WAAO,OAAO;AAAA,EAChB;AAAA,EAEQ,kBAA0C;AAChD,WAAO,QAAQ,OAAO,OAAO,CAAC,EAAE,OAAO,SAAS,WAAW,GAAG,KAAK,MAAM;AACvE,UAAI,QAAQ,YAAY,QAAQ,QAAQ;AACtC,aAAK,QAAQ,IAAI,QAAQ,OAAO;AAAA,MAClC;AAEA,YAAM,aAAa,OAAO,QAAQ,IAAI,EACnC,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AACrB,eAAO,GAAG,GAAG,KAAK,KAAK;AAAA,MACzB,CAAC,EACA,KAAK,KAAK;AAEb,UAAI,UAAU,SAAS;AACrB,YAAI,KAAK,qBAAqB;AAC5B,gBAAM,eAAe,OAAO,YAAY,WAAW,UAAU,KAAK,UAAU,OAAO;AAEnF,iBAAO,iBAAiB,IAAI,MAAM,YAAY,CAAC;AAAA,QACjD;AAAA,MACF;AAEA,aAAO,IAAI,SAAS,KAAK,KAAK,KAAK,OAAO,GAAG,aAAa,KAAK,UAAU,MAAM,EAAE;AAAA,IACnF,CAAC;AAAA,EACH;AAAA,EAEO,WAAW,EAAE,WAAW,YAAY,GAAqD;AAC9F,QAAI,CAAC,WAAW;AACd,WAAK,OAAO,KAAK,6CAA6C;AAE9D;AAAA,IACF;AAEA,WAAO,KAAK;AAAA,MACV,KAAK;AAAA,MACL,cAAc,CAAC,yBAAyB,CAAC;AAAA,MACzC,kBAAkB;AAAA,MAClB;AAAA,IACF,CAAC;AAED,SAAK,sBAAsB;AAAA,EAC7B;AAAA,EAEO,IAAI;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,
|
|
4
|
+
"sourcesContent": ["import * as Sentry from '@sentry/node';\nimport { nodeProfilingIntegration } from '@sentry/profiling-node';\nimport cluster from 'node:cluster';\nimport winston from 'winston';\nimport type { LogOptions } from '../websocket/utils.js';\n\nexport type LoggerLevels =\n | 'error'\n | 'warn'\n | 'info'\n | 'command'\n | 'database'\n | 'redis'\n | 'webServer'\n | 'webSocket'\n | 'queue'\n | 'queueJob'\n | 'event'\n | 'debug';\n\nexport class Logger {\n private static instance: Logger;\n private logger: winston.Logger;\n\n private environment: string | undefined;\n\n public isSentryInitialized = false;\n\n private constructor() {\n this.environment = process.env.NODE_ENV;\n\n const customFormat = this.getCustomFormat();\n\n const customLevels: winston.config.AbstractConfigSetLevels = {\n error: 0,\n warn: 1,\n info: 2,\n command: 3,\n database: 4,\n redis: 5,\n webServer: 6,\n webSocket: 7,\n queue: 8,\n queueJob: 9,\n event: 10,\n debug: 11,\n };\n\n const customColors: winston.config.AbstractConfigSetColors = {\n error: 'red',\n warn: 'yellow',\n info: 'blue',\n command: 'cyan',\n database: 'brightGreen',\n redis: 'brightYellow',\n webServer: 'brightBlue',\n webSocket: 'brightMagenta',\n queue: 'gray',\n queueJob: 'blue',\n event: 'brightGreen',\n debug: 'brightCyan',\n };\n\n winston.addColors(customColors);\n\n this.logger = winston.createLogger({\n levels: customLevels,\n level: this.environment === 'production' ? 'info' : 'debug',\n format: winston.format.combine(\n winston.format.timestamp({\n format: 'YYYY-MM-DD HH:mm:ss',\n }),\n winston.format.errors({ stack: true }),\n winston.format.splat(),\n winston.format.json(),\n ),\n transports: [\n new winston.transports.Console({\n format: winston.format.combine(winston.format.colorize(), customFormat),\n }),\n ],\n });\n }\n\n public static getInstance(): Logger {\n if (!Logger.instance) {\n Logger.instance = new Logger();\n }\n\n return Logger.instance;\n }\n\n private getCustomFormat(): winston.Logform.Format {\n return winston.format.printf(({ level, message, timestamp, ...meta }) => {\n if (cluster.isWorker && cluster.worker) {\n meta['Worker'] = cluster.worker.id; // .process.pid;\n }\n\n const metaString = Object.entries(meta)\n .map(([key, value]) => {\n return `${key}: ${value}`;\n })\n .join(' | ');\n\n if (level === 'error') {\n if (this.isSentryInitialized) {\n const errorMessage = typeof message === 'string' ? message : JSON.stringify(message);\n\n Sentry.captureException(new Error(errorMessage));\n }\n }\n\n return `[${timestamp}] ${level}: ${message}${metaString ? ` (${metaString})` : ''}`;\n });\n }\n\n public initSentry({ sentryDsn, environment }: { sentryDsn: string; environment: string }): void {\n if (!sentryDsn) {\n this.logger.warn('Missing Sentry DSN when initializing Sentry');\n\n return;\n }\n\n Sentry.init({\n dsn: sentryDsn,\n integrations: [nodeProfilingIntegration()],\n tracesSampleRate: 1.0,\n environment,\n });\n\n this.isSentryInitialized = true;\n }\n\n public log({\n level,\n message,\n meta,\n options: _options,\n }: {\n level: LoggerLevels;\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n // if (options?.muteWorker) {\n // }\n\n if (message instanceof Error) {\n const errorMessage = message.stack ?? message.toString();\n this.logger.log(level, errorMessage, meta);\n } else if (typeof message === 'string') {\n this.logger.log(level, message, meta);\n } else {\n this.logger.log(level, JSON.stringify(message), meta);\n }\n }\n\n public debug({\n message,\n meta,\n options,\n }: {\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void;\n public debug(message: unknown, meta?: Record<string, unknown>): void;\n public debug(\n messageOrOptions: unknown | { message: unknown; meta?: Record<string, unknown>; options?: LogOptions },\n meta?: Record<string, unknown>,\n ): void {\n if (typeof messageOrOptions === 'object' && messageOrOptions !== null && 'message' in messageOrOptions) {\n const {\n message,\n meta: optionsMeta,\n options,\n } = messageOrOptions as { message: unknown; meta?: Record<string, unknown>; options?: LogOptions };\n this.log({ level: 'debug', message, meta: optionsMeta, options });\n } else {\n this.log({ level: 'debug', message: messageOrOptions, meta, options: undefined });\n }\n }\n\n public info({\n message,\n meta,\n options,\n }: {\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void;\n public info(message: unknown, meta?: Record<string, unknown>): void;\n public info(\n messageOrOptions: unknown | { message: unknown; meta?: Record<string, unknown>; options?: LogOptions },\n meta?: Record<string, unknown>,\n ): void {\n if (typeof messageOrOptions === 'object' && messageOrOptions !== null && 'message' in messageOrOptions) {\n const {\n message,\n meta: optionsMeta,\n options,\n } = messageOrOptions as { message: unknown; meta?: Record<string, unknown>; options?: LogOptions };\n this.log({ level: 'info', message, meta: optionsMeta, options });\n } else {\n this.log({ level: 'info', message: messageOrOptions, meta, options: undefined });\n }\n }\n\n public warn({\n message,\n meta,\n options,\n }: {\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void;\n public warn(message: unknown, meta?: Record<string, unknown>): void;\n public warn(\n messageOrOptions: unknown | { message: unknown; meta?: Record<string, unknown>; options?: LogOptions },\n meta?: Record<string, unknown>,\n ): void {\n if (typeof messageOrOptions === 'object' && messageOrOptions !== null && 'message' in messageOrOptions) {\n const {\n message,\n meta: optionsMeta,\n options,\n } = messageOrOptions as { message: unknown; meta?: Record<string, unknown>; options?: LogOptions };\n this.log({ level: 'warn', message, meta: optionsMeta, options });\n } else {\n this.log({ level: 'warn', message: messageOrOptions, meta, options: undefined });\n }\n }\n\n public error({\n error,\n message,\n meta,\n options,\n }: {\n error: Error | unknown;\n message?: string;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n if (message) {\n // If a message is provided, combine it with the error for better context\n const errorMessage = error instanceof Error ? error.message : String(error);\n const combinedMessage = `${message}: ${errorMessage}`;\n this.log({ level: 'error', message: combinedMessage, meta, options });\n\n // Also capture the original error for Sentry if it's an Error object\n if (error instanceof Error && this.isSentryInitialized) {\n Sentry.captureException(error);\n }\n } else {\n // Original behavior when no message is provided\n this.log({ level: 'error', message: error, meta, options });\n }\n }\n\n public custom({\n level,\n message,\n meta,\n options,\n }: {\n level: LoggerLevels;\n message: unknown;\n meta?: Record<string, unknown>;\n options?: LogOptions;\n }): void {\n this.log({ level, message, meta, options });\n }\n}\n\nexport default Logger.getInstance();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -22,7 +22,7 @@ class WebSocketBase {
|
|
|
22
22
|
const controllers = await Loader.loadModulesInDirectory({
|
|
23
23
|
directory: controllersDirectory,
|
|
24
24
|
// NOTE:
|
|
25
|
-
// When getting "system", it gets /app/node_modules/@
|
|
25
|
+
// When getting "system", it gets /app/node_modules/@scpxl/nodejs-framework/dist/websocket/controllers/server
|
|
26
26
|
// Therefor .js is needed also
|
|
27
27
|
// Fix so only .ts vs .js is needed
|
|
28
28
|
extensions: [".ts", ".js"]
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/websocket/websocket-base.ts"],
|
|
4
|
-
"sourcesContent": ["import { existsSync } from 'fs';\nimport type { WebSocketMessageHandler, WebSocketRoute, WebSocketType } from './websocket.interface.js';\nimport { getRouteKey, log, parseServerMessage } from './utils.js';\nimport type { WebSocketServerBaseControllerType } from './controller/server/base.interface.js';\nimport type { WebSocketClientBaseControllerType } from './controller/client/base.interface.js';\nimport type WebSocket from 'ws';\nimport { Helper, Loader } from '../util/index.js';\n\nexport default abstract class WebSocketBase {\n protected routes: WebSocketRoute[] = [];\n protected routeHandlers: Map<string, WebSocketMessageHandler> = new Map();\n\n protected defaultRoutes: WebSocketRoute[] = [];\n\n public abstract get type(): WebSocketType;\n\n protected abstract getControllerDependencies(): any;\n protected abstract shouldPrintRoutes(): boolean;\n protected abstract handleMessageError(clientId: string, error: string): void;\n\n protected async configureRoutes(routes: WebSocketRoute[], controllersDirectory: string): Promise<void> {\n // log ('Configuring routes', { Type: this.type, 'Controllers Directory': controllersDirectory });\n\n const controllersDirectoryExists = await existsSync(controllersDirectory);\n\n if (!controllersDirectoryExists) {\n log('Controllers directory not found', {\n Directory: controllersDirectory,\n });\n\n return;\n }\n\n const scriptFileExtension = Helper.getScriptFileExtension();\n\n // Load controllers\n const controllers = await Loader.loadModulesInDirectory({\n directory: controllersDirectory,\n // NOTE:\n // When getting \"system\", it gets /app/node_modules/@
|
|
4
|
+
"sourcesContent": ["import { existsSync } from 'fs';\nimport type { WebSocketMessageHandler, WebSocketRoute, WebSocketType } from './websocket.interface.js';\nimport { getRouteKey, log, parseServerMessage } from './utils.js';\nimport type { WebSocketServerBaseControllerType } from './controller/server/base.interface.js';\nimport type { WebSocketClientBaseControllerType } from './controller/client/base.interface.js';\nimport type WebSocket from 'ws';\nimport { Helper, Loader } from '../util/index.js';\n\nexport default abstract class WebSocketBase {\n protected routes: WebSocketRoute[] = [];\n protected routeHandlers: Map<string, WebSocketMessageHandler> = new Map();\n\n protected defaultRoutes: WebSocketRoute[] = [];\n\n public abstract get type(): WebSocketType;\n\n protected abstract getControllerDependencies(): any;\n protected abstract shouldPrintRoutes(): boolean;\n protected abstract handleMessageError(clientId: string, error: string): void;\n\n protected async configureRoutes(routes: WebSocketRoute[], controllersDirectory: string): Promise<void> {\n // log ('Configuring routes', { Type: this.type, 'Controllers Directory': controllersDirectory });\n\n const controllersDirectoryExists = await existsSync(controllersDirectory);\n\n if (!controllersDirectoryExists) {\n log('Controllers directory not found', {\n Directory: controllersDirectory,\n });\n\n return;\n }\n\n const scriptFileExtension = Helper.getScriptFileExtension();\n\n // Load controllers\n const controllers = await Loader.loadModulesInDirectory({\n directory: controllersDirectory,\n // NOTE:\n // When getting \"system\", it gets /app/node_modules/@scpxl/nodejs-framework/dist/websocket/controllers/server\n // Therefor .js is needed also\n // Fix so only .ts vs .js is needed\n extensions: ['.ts', '.js'],\n });\n\n for (const route of routes) {\n let ControllerClass: WebSocketServerBaseControllerType | WebSocketClientBaseControllerType;\n\n // log('Registering route', {\n // Type: route.type,\n // Controller: route.controller ? route.controller.toString() : route.controllerName,\n // Action: route.action,\n // });\n\n if (route.controller) {\n ControllerClass = route.controller;\n } else if (route.controllerName) {\n ControllerClass = controllers[route.controllerName];\n } else {\n throw new Error('WebSocket controller config not found');\n }\n\n if (typeof ControllerClass !== 'function') {\n log('Controller not found', {\n Controller: route.controllerName,\n Path: `${controllersDirectory}/${route.controllerName}.${scriptFileExtension}`,\n });\n\n continue;\n }\n\n const controllerDependencies = this.getControllerDependencies();\n\n const controllerInstance = new ControllerClass(controllerDependencies);\n\n const controllerHandler = controllerInstance[\n route.action as keyof typeof controllerInstance\n ] as WebSocketMessageHandler;\n const routeKey = getRouteKey(route.type, route.action);\n\n this.routeHandlers.set(routeKey, controllerHandler);\n }\n\n if (this.shouldPrintRoutes()) {\n log('Routes:', { Type: this.type });\n\n this.printRoutes();\n }\n }\n\n protected async handleServerMessage(ws: WebSocket, message: WebSocket.Data, clientId: string): Promise<void | any> {\n try {\n const parsedMessage = parseServerMessage(message);\n const type = parsedMessage.type;\n const action = parsedMessage.action;\n\n log('Incoming message', {\n 'Client ID': clientId,\n Type: type ?? '-',\n Action: action ?? '-',\n });\n\n const routeKey = getRouteKey(parsedMessage.type as string, parsedMessage.action as string);\n\n const messageHandler = this.routeHandlers.get(routeKey);\n\n if (messageHandler) {\n const messageResponse = await messageHandler(ws, clientId, parsedMessage.data);\n\n return {\n type,\n action,\n response: messageResponse,\n };\n }\n // throw new Error(`Route handler not found (Route Key: ${routeKey} | Type: ${type} | Action: ${action})`);\n\n log('Route handler not found', {\n RouteKey: routeKey,\n Type: type,\n Action: action,\n });\n\n // if (\n // typeof this.applicationConfig.webSocket\n // ?.serverMessageHandler === 'function'\n // ) {\n // // Execute custom application subscriber event handler\n // this.applicationConfig.webSocket.serverMessageHandler(\n // {\n // ws,\n // clientId,\n // parsedMessage,\n // },\n // );\n // }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n\n log(errorMessage);\n\n this.handleMessageError(clientId, errorMessage);\n }\n }\n\n protected printRoutes(): void {\n let routesString = '';\n\n const routeKeys = Array.from(this.routeHandlers.keys());\n\n routeKeys.forEach((routeKey, index) => {\n const [type, action] = routeKey.split(':');\n\n routesString += `Type: ${type} -> Action: ${action}`;\n\n if (index !== routeKeys.length - 1) {\n routesString += '\\n';\n }\n });\n\n log(routesString);\n }\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scpxl/nodejs-framework",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "PXL Node.js Framework",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=22.0.0"
|
|
@@ -69,21 +69,25 @@
|
|
|
69
69
|
"build": "npm run clean && node esbuild.config.js",
|
|
70
70
|
"build:tsc": "npm run clean && tsc",
|
|
71
71
|
"build:local": "npm run clean && node esbuild.config.js && yalc push",
|
|
72
|
-
"docs": "
|
|
72
|
+
"docs:dev": "vitepress dev docs",
|
|
73
|
+
"docs:build": "vitepress build docs",
|
|
74
|
+
"docs:preview": "vitepress preview docs",
|
|
73
75
|
"release": "node scripts/release.js",
|
|
74
76
|
"prepare": "husky",
|
|
77
|
+
"postinstall": "npm run build || echo 'Build failed, dist files may be missing'",
|
|
75
78
|
"lint": "eslint .",
|
|
76
79
|
"lint:fix": "eslint . --fix",
|
|
77
|
-
"
|
|
78
|
-
"
|
|
80
|
+
"prettier": "prettier --check .",
|
|
81
|
+
"prettier:fix": "prettier --write .",
|
|
79
82
|
"typecheck": "tsc --noEmit",
|
|
80
|
-
"test": "
|
|
81
|
-
"test:watch": "
|
|
82
|
-
"test:coverage": "
|
|
83
|
-
"test:unit": "
|
|
84
|
-
"test:integration": "
|
|
85
|
-
"test:e2e": "
|
|
86
|
-
"
|
|
83
|
+
"test": "vitest run",
|
|
84
|
+
"test:watch": "vitest",
|
|
85
|
+
"test:coverage": "vitest run --coverage",
|
|
86
|
+
"test:unit": "vitest run test/unit",
|
|
87
|
+
"test:integration": "vitest run test/integration",
|
|
88
|
+
"test:e2e": "vitest run test/e2e",
|
|
89
|
+
"test:ui": "vitest --ui",
|
|
90
|
+
"check-all": "npm run lint && npm run prettier && npm run typecheck",
|
|
87
91
|
"yalc:publish": "yalc publish",
|
|
88
92
|
"yalc:push": "yalc push",
|
|
89
93
|
"dev:yalc": "npm run build && npm run yalc:push"
|
|
@@ -100,6 +104,8 @@
|
|
|
100
104
|
"@types/yargs": "^17.0.33",
|
|
101
105
|
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
|
102
106
|
"@typescript-eslint/parser": "^8.1.0",
|
|
107
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
108
|
+
"@vitest/ui": "^3.2.4",
|
|
103
109
|
"del-cli": "^6.0.0",
|
|
104
110
|
"esbuild": "^0.25.5",
|
|
105
111
|
"eslint": "^9.9.0",
|
|
@@ -115,7 +121,8 @@
|
|
|
115
121
|
"ts-node": "^10.9.2",
|
|
116
122
|
"tsc-alias": "^1.8.16",
|
|
117
123
|
"tsconfig-paths": "^4.2.0",
|
|
118
|
-
"
|
|
124
|
+
"vitepress": "^1.6.4",
|
|
125
|
+
"vitest": "^3.2.4"
|
|
119
126
|
},
|
|
120
127
|
"dependencies": {
|
|
121
128
|
"@aws-sdk/client-s3": "^3.496.0",
|