@scpxl/nodejs-framework 1.0.22 → 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.
- package/README.md +264 -26
- package/dist/api-requester/api-requester.d.ts +32 -0
- package/dist/api-requester/api-requester.d.ts.map +1 -0
- package/dist/api-requester/api-requester.js +2 -1
- package/dist/api-requester/api-requester.js.map +2 -2
- package/dist/api-requester/index.d.ts +3 -0
- package/dist/api-requester/index.d.ts.map +1 -0
- package/dist/application/base-application.d.ts +106 -0
- package/dist/application/base-application.d.ts.map +1 -0
- package/dist/application/base-application.interface.d.ts +162 -0
- package/dist/application/base-application.interface.d.ts.map +1 -0
- package/dist/application/base-application.js +7 -3
- package/dist/application/base-application.js.map +2 -2
- package/dist/application/command-application.d.ts +18 -0
- package/dist/application/command-application.d.ts.map +1 -0
- package/dist/application/command-application.interface.d.ts +26 -0
- package/dist/application/command-application.interface.d.ts.map +1 -0
- package/dist/application/index.d.ts +5 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/web-application.d.ts +43 -0
- package/dist/application/web-application.d.ts.map +1 -0
- package/dist/application/web-application.interface.d.ts +21 -0
- package/dist/application/web-application.interface.d.ts.map +1 -0
- package/dist/application/web-application.js +1 -0
- package/dist/application/web-application.js.map +2 -2
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/jwt.d.ts +25 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/cache/index.d.ts +2 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/manager.d.ts +107 -0
- package/dist/cache/manager.d.ts.map +1 -0
- package/dist/cache/manager.js +2 -1
- package/dist/cache/manager.js.map +2 -2
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +12591 -0
- package/dist/cli/index.js.map +7 -0
- package/dist/cluster/cluster-manager.d.ts +18 -0
- package/dist/cluster/cluster-manager.d.ts.map +1 -0
- package/dist/cluster/cluster-manager.interface.d.ts +23 -0
- package/dist/cluster/cluster-manager.interface.d.ts.map +1 -0
- package/dist/cluster/cluster-manager.js +45 -8
- package/dist/cluster/cluster-manager.js.map +2 -2
- package/dist/cluster/index.d.ts +2 -0
- package/dist/cluster/index.d.ts.map +1 -0
- package/dist/command/command-manager.d.ts +19 -0
- package/dist/command/command-manager.d.ts.map +1 -0
- package/dist/command/command.d.ts +27 -0
- package/dist/command/command.d.ts.map +1 -0
- package/dist/command/command.interface.d.ts +11 -0
- package/dist/command/command.interface.d.ts.map +1 -0
- package/dist/command/index.d.ts +3 -0
- package/dist/command/index.d.ts.map +1 -0
- package/dist/config/env.d.ts +11 -0
- package/dist/config/env.d.ts.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/schema.d.ts +432 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/database/dynamic-entity-form-decorators.d.ts +31 -0
- package/dist/database/dynamic-entity-form-decorators.d.ts.map +1 -0
- package/dist/database/dynamic-entity-form-decorators.js.map +1 -1
- package/dist/database/dynamic-entity.d.ts +18 -0
- package/dist/database/dynamic-entity.d.ts.map +1 -0
- package/dist/database/dynamic-entity.js +11 -1
- package/dist/database/dynamic-entity.js.map +2 -2
- package/dist/database/index.d.ts +5 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/instance.d.ts +36 -0
- package/dist/database/instance.d.ts.map +1 -0
- package/dist/database/instance.interface.d.ts +5 -0
- package/dist/database/instance.interface.d.ts.map +1 -0
- package/dist/database/manager.d.ts +27 -0
- package/dist/database/manager.d.ts.map +1 -0
- package/dist/database/manager.interface.d.ts +18 -0
- package/dist/database/manager.interface.d.ts.map +1 -0
- package/dist/database/manager.js +3 -2
- package/dist/database/manager.js.map +2 -2
- package/dist/error/error-reporter.d.ts +109 -0
- package/dist/error/error-reporter.d.ts.map +1 -0
- package/dist/error/error-reporter.js +32 -29
- package/dist/error/error-reporter.js.map +2 -2
- package/dist/error/error.interface.d.ts +126 -0
- package/dist/error/error.interface.d.ts.map +1 -0
- package/dist/error/framework-errors.d.ts +113 -0
- package/dist/error/framework-errors.d.ts.map +1 -0
- package/dist/error/index.d.ts +6 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +3 -2
- package/dist/error/index.js.map +2 -2
- package/dist/event/controller/base.d.ts +23 -0
- package/dist/event/controller/base.d.ts.map +1 -0
- package/dist/event/controller/base.interface.d.ts +11 -0
- package/dist/event/controller/base.interface.d.ts.map +1 -0
- package/dist/event/controller/base.js +2 -1
- package/dist/event/controller/base.js.map +2 -2
- package/dist/event/index.d.ts +5 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/event/manager.d.ts +21 -0
- package/dist/event/manager.d.ts.map +1 -0
- package/dist/event/manager.interface.d.ts +137 -0
- package/dist/event/manager.interface.d.ts.map +1 -0
- package/dist/event/manager.js +5 -4
- package/dist/event/manager.js.map +2 -2
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/lifecycle/exit.d.ts +11 -0
- package/dist/lifecycle/exit.d.ts.map +1 -0
- package/dist/lifecycle/exit.js.map +2 -2
- package/dist/lifecycle/index.d.ts +7 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-manager.d.ts +66 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-manager.js +6 -11
- package/dist/lifecycle/lifecycle-manager.js.map +2 -2
- package/dist/lifecycle/shutdown-controller.d.ts +15 -0
- package/dist/lifecycle/shutdown-controller.d.ts.map +1 -0
- package/dist/lifecycle/types.d.ts +28 -0
- package/dist/lifecycle/types.d.ts.map +1 -0
- package/dist/logger/index.d.ts +2 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/logger.d.ts +59 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.interface.d.ts +2 -0
- package/dist/logger/logger.interface.d.ts.map +1 -0
- package/dist/logger/logger.js +11 -3
- package/dist/logger/logger.js.map +2 -2
- package/dist/performance/cache-performance.d.ts +64 -0
- package/dist/performance/cache-performance.d.ts.map +1 -0
- package/dist/performance/database-performance.d.ts +40 -0
- package/dist/performance/database-performance.d.ts.map +1 -0
- package/dist/performance/index.d.ts +8 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/performance-monitor.d.ts +68 -0
- package/dist/performance/performance-monitor.d.ts.map +1 -0
- package/dist/performance/performance-monitor.js +10 -3
- package/dist/performance/performance-monitor.js.map +2 -2
- package/dist/performance/performance-monitor.plugin.d.ts +24 -0
- package/dist/performance/performance-monitor.plugin.d.ts.map +1 -0
- package/dist/performance/queue-performance.d.ts +46 -0
- package/dist/performance/queue-performance.d.ts.map +1 -0
- package/dist/performance/webserver-performance.d.ts +69 -0
- package/dist/performance/webserver-performance.d.ts.map +1 -0
- package/dist/performance/websocket-performance.d.ts +44 -0
- package/dist/performance/websocket-performance.d.ts.map +1 -0
- package/dist/queue/index.d.ts +6 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.interface.d.ts +10 -0
- package/dist/queue/index.interface.d.ts.map +1 -0
- package/dist/queue/job.interface.d.ts +43 -0
- package/dist/queue/job.interface.d.ts.map +1 -0
- package/dist/queue/manager.d.ts +44 -0
- package/dist/queue/manager.d.ts.map +1 -0
- package/dist/queue/manager.interface.d.ts +18 -0
- package/dist/queue/manager.interface.d.ts.map +1 -0
- package/dist/queue/processor/base.d.ts +29 -0
- package/dist/queue/processor/base.d.ts.map +1 -0
- package/dist/queue/processor/base.js +2 -1
- package/dist/queue/processor/base.js.map +2 -2
- package/dist/queue/processor/processor.interface.d.ts +16 -0
- package/dist/queue/processor/processor.interface.d.ts.map +1 -0
- package/dist/queue/worker.d.ts +14 -0
- package/dist/queue/worker.d.ts.map +1 -0
- package/dist/queue/worker.interface.d.ts +13 -0
- package/dist/queue/worker.interface.d.ts.map +1 -0
- package/dist/redis/index.d.ts +3 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/redis/instance.d.ts +32 -0
- package/dist/redis/instance.d.ts.map +1 -0
- package/dist/redis/instance.interface.d.ts +9 -0
- package/dist/redis/instance.interface.d.ts.map +1 -0
- package/dist/redis/manager.d.ts +15 -0
- package/dist/redis/manager.d.ts.map +1 -0
- package/dist/redis/manager.interface.d.ts +8 -0
- package/dist/redis/manager.interface.d.ts.map +1 -0
- package/dist/redis/manager.js +16 -16
- package/dist/redis/manager.js.map +2 -2
- package/dist/request-context/index.d.ts +3 -0
- package/dist/request-context/index.d.ts.map +1 -0
- package/dist/request-context/request-context.d.ts +108 -0
- package/dist/request-context/request-context.d.ts.map +1 -0
- package/dist/request-context/request-context.interface.d.ts +46 -0
- package/dist/request-context/request-context.interface.d.ts.map +1 -0
- package/dist/schemas/common.d.ts +197 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +108 -0
- package/dist/schemas/common.js.map +7 -0
- package/dist/schemas/index.d.ts +6 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +2 -0
- package/dist/schemas/index.js.map +7 -0
- package/dist/services/aws/index.d.ts +2 -0
- package/dist/services/aws/index.d.ts.map +1 -0
- package/dist/services/aws/s3.d.ts +54 -0
- package/dist/services/aws/s3.d.ts.map +1 -0
- package/dist/services/aws/s3.interface.d.ts +14 -0
- package/dist/services/aws/s3.interface.d.ts.map +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/util/file.d.ts +58 -0
- package/dist/util/file.d.ts.map +1 -0
- package/dist/util/helper.d.ts +51 -0
- package/dist/util/helper.d.ts.map +1 -0
- package/dist/util/helper.js +72 -10
- package/dist/util/helper.js.map +2 -2
- package/dist/util/image.d.ts +12 -0
- package/dist/util/image.d.ts.map +1 -0
- package/dist/util/index.d.ts +11 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/loader.d.ts +21 -0
- package/dist/util/loader.d.ts.map +1 -0
- package/dist/util/loader.js +5 -2
- package/dist/util/loader.js.map +2 -2
- package/dist/util/num.d.ts +13 -0
- package/dist/util/num.d.ts.map +1 -0
- package/dist/util/os.d.ts +6 -0
- package/dist/util/os.d.ts.map +1 -0
- package/dist/util/str.d.ts +39 -0
- package/dist/util/str.d.ts.map +1 -0
- package/dist/util/time.d.ts +19 -0
- package/dist/util/time.d.ts.map +1 -0
- package/dist/util/time.interface.d.ts +12 -0
- package/dist/util/time.interface.d.ts.map +1 -0
- package/dist/util/timing.d.ts +36 -0
- package/dist/util/timing.d.ts.map +1 -0
- package/dist/util/timing.interface.d.ts +47 -0
- package/dist/util/timing.interface.d.ts.map +1 -0
- package/dist/util/url.d.ts +7 -0
- package/dist/util/url.d.ts.map +1 -0
- package/dist/webserver/controller/auth-middleware.d.ts +21 -0
- package/dist/webserver/controller/auth-middleware.d.ts.map +1 -0
- package/dist/webserver/controller/base.d.ts +41 -0
- package/dist/webserver/controller/base.d.ts.map +1 -0
- package/dist/webserver/controller/base.interface.d.ts +50 -0
- package/dist/webserver/controller/base.interface.d.ts.map +1 -0
- package/dist/webserver/controller/base.js +4 -4
- package/dist/webserver/controller/base.js.map +2 -2
- package/dist/webserver/controller/entity.d.ts +94 -0
- package/dist/webserver/controller/entity.d.ts.map +1 -0
- package/dist/webserver/controller/entity.js.map +2 -2
- package/dist/webserver/controller/example-auth.d.ts +12 -0
- package/dist/webserver/controller/example-auth.d.ts.map +1 -0
- package/dist/webserver/controller/health.d.ts +13 -0
- package/dist/webserver/controller/health.d.ts.map +1 -0
- package/dist/webserver/controller/health.js +0 -14
- package/dist/webserver/controller/health.js.map +2 -2
- package/dist/webserver/define-action.d.ts +26 -0
- package/dist/webserver/define-action.d.ts.map +1 -0
- package/dist/webserver/define-action.js +16 -0
- package/dist/webserver/define-action.js.map +7 -0
- package/dist/webserver/define-route.d.ts +53 -0
- package/dist/webserver/define-route.d.ts.map +1 -0
- package/dist/webserver/define-route.js +11 -6
- package/dist/webserver/define-route.js.map +2 -2
- package/dist/webserver/index.d.ts +14 -0
- package/dist/webserver/index.d.ts.map +1 -0
- package/dist/webserver/index.js +2 -0
- package/dist/webserver/index.js.map +2 -2
- package/dist/webserver/util.d.ts +10 -0
- package/dist/webserver/util.d.ts.map +1 -0
- package/dist/webserver/util.js +5 -2
- package/dist/webserver/util.js.map +2 -2
- package/dist/webserver/webserver.d.ts +93 -0
- package/dist/webserver/webserver.d.ts.map +1 -0
- package/dist/webserver/webserver.interface.d.ts +181 -0
- package/dist/webserver/webserver.interface.d.ts.map +1 -0
- package/dist/webserver/webserver.interface.js.map +1 -1
- package/dist/webserver/webserver.js +30 -33
- package/dist/webserver/webserver.js.map +2 -2
- package/dist/websocket/controller/client/base.d.ts +12 -0
- package/dist/websocket/controller/client/base.d.ts.map +1 -0
- package/dist/websocket/controller/client/base.interface.d.ts +12 -0
- package/dist/websocket/controller/client/base.interface.d.ts.map +1 -0
- package/dist/websocket/controller/server/base.d.ts +13 -0
- package/dist/websocket/controller/server/base.d.ts.map +1 -0
- package/dist/websocket/controller/server/base.interface.d.ts +13 -0
- package/dist/websocket/controller/server/base.interface.d.ts.map +1 -0
- package/dist/websocket/controllers/client/system.d.ts +6 -0
- package/dist/websocket/controllers/client/system.d.ts.map +1 -0
- package/dist/websocket/controllers/server/system.d.ts +7 -0
- package/dist/websocket/controllers/server/system.d.ts.map +1 -0
- package/dist/websocket/index.d.ts +9 -0
- package/dist/websocket/index.d.ts.map +1 -0
- package/dist/websocket/index.js +2 -0
- package/dist/websocket/index.js.map +2 -2
- package/dist/websocket/routes/client/system.d.ts +3 -0
- package/dist/websocket/routes/client/system.d.ts.map +1 -0
- package/dist/websocket/routes/server/system.d.ts +3 -0
- package/dist/websocket/routes/server/system.d.ts.map +1 -0
- package/dist/websocket/utils.d.ts +9 -0
- package/dist/websocket/utils.d.ts.map +1 -0
- package/dist/websocket/websocket-auth.d.ts +17 -0
- package/dist/websocket/websocket-auth.d.ts.map +1 -0
- package/dist/websocket/websocket-auth.js +46 -0
- package/dist/websocket/websocket-auth.js.map +7 -0
- package/dist/websocket/websocket-base.d.ts +19 -0
- package/dist/websocket/websocket-base.d.ts.map +1 -0
- package/dist/websocket/websocket-client-manager.d.ts +53 -0
- package/dist/websocket/websocket-client-manager.d.ts.map +1 -0
- package/dist/websocket/websocket-client-manager.interface.d.ts +8 -0
- package/dist/websocket/websocket-client-manager.interface.d.ts.map +1 -0
- package/dist/websocket/websocket-client-manager.js +6 -5
- package/dist/websocket/websocket-client-manager.js.map +2 -2
- package/dist/websocket/websocket-client.d.ts +64 -0
- package/dist/websocket/websocket-client.d.ts.map +1 -0
- package/dist/websocket/websocket-client.interface.d.ts +14 -0
- package/dist/websocket/websocket-client.interface.d.ts.map +1 -0
- package/dist/websocket/websocket-client.js +97 -3
- package/dist/websocket/websocket-client.js.map +2 -2
- package/dist/websocket/websocket-room-manager.d.ts +32 -0
- package/dist/websocket/websocket-room-manager.d.ts.map +1 -0
- package/dist/websocket/websocket-server.d.ts +102 -0
- package/dist/websocket/websocket-server.d.ts.map +1 -0
- package/dist/websocket/websocket-server.interface.d.ts +16 -0
- package/dist/websocket/websocket-server.interface.d.ts.map +1 -0
- package/dist/websocket/websocket-server.js +62 -50
- package/dist/websocket/websocket-server.js.map +2 -2
- package/dist/websocket/websocket-service.d.ts +44 -0
- package/dist/websocket/websocket-service.d.ts.map +1 -0
- package/dist/websocket/websocket.interface.d.ts +137 -0
- package/dist/websocket/websocket.interface.d.ts.map +1 -0
- package/dist/websocket/websocket.interface.js.map +2 -2
- package/package.json +21 -24
- package/pxl.js +0 -4
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
|
|
@@ -152,33 +183,146 @@ await app.queue.manager.addJobToQueue({
|
|
|
152
183
|
});
|
|
153
184
|
```
|
|
154
185
|
|
|
186
|
+
### Simple Load Test
|
|
187
|
+
|
|
188
|
+
Run lightweight load against any endpoint while iterating locally:
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm run load:test -- --url http://localhost:3000/health --requests 200 --concurrency 10
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Switch to a time-based stream for soak-style checks:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm run load:test -- --url http://localhost:3000/api/users --duration 30 --concurrency 8 --method POST --body '{"name":"Test"}' --header 'Content-Type: application/json'
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The script reports latency percentiles, status code counts, and a few failure samples for quick feedback.
|
|
201
|
+
|
|
155
202
|
### WebSocket Server
|
|
156
203
|
|
|
204
|
+
Real-time bidirectional communication with room support and authentication:
|
|
205
|
+
|
|
157
206
|
```typescript
|
|
158
207
|
import { WebApplication } from '@scpxl/nodejs-framework';
|
|
208
|
+
import { WebSocketServerBaseController } from '@scpxl/nodejs-framework/websocket';
|
|
209
|
+
import type { WebSocket } from 'ws';
|
|
210
|
+
|
|
211
|
+
// Create WebSocket controller
|
|
212
|
+
class ChatController extends WebSocketServerBaseController {
|
|
213
|
+
public send = (ws: WebSocket, clientId: string, data: any) => {
|
|
214
|
+
// Broadcast to all clients
|
|
215
|
+
this.webSocketServer.sendMessageToAll({
|
|
216
|
+
data: {
|
|
217
|
+
type: 'chat',
|
|
218
|
+
action: 'message',
|
|
219
|
+
data: {
|
|
220
|
+
clientId,
|
|
221
|
+
text: data.text,
|
|
222
|
+
timestamp: new Date().toISOString(),
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
return { success: true };
|
|
228
|
+
};
|
|
229
|
+
}
|
|
159
230
|
|
|
160
231
|
const app = new WebApplication({
|
|
161
232
|
name: 'chat-app',
|
|
162
233
|
webserver: { port: 3000 },
|
|
163
|
-
websocket: {
|
|
234
|
+
websocket: {
|
|
235
|
+
enabled: true,
|
|
236
|
+
type: 'server',
|
|
237
|
+
url: 'ws://localhost:3000/ws',
|
|
238
|
+
controllersDirectory: './controllers',
|
|
239
|
+
routes: [
|
|
240
|
+
{
|
|
241
|
+
type: 'chat',
|
|
242
|
+
action: 'send',
|
|
243
|
+
controllerName: 'chat',
|
|
244
|
+
controller: ChatController,
|
|
245
|
+
},
|
|
246
|
+
],
|
|
247
|
+
// Optional: JWT authentication
|
|
248
|
+
// Clients connect with: ws://localhost:3000/ws?token=<jwt>
|
|
249
|
+
events: {
|
|
250
|
+
onConnected: ({ ws, clientId }) => {
|
|
251
|
+
console.log('Client connected:', clientId);
|
|
252
|
+
ws.send(
|
|
253
|
+
JSON.stringify({
|
|
254
|
+
type: 'system',
|
|
255
|
+
action: 'connected',
|
|
256
|
+
data: { clientId, message: 'Welcome!' },
|
|
257
|
+
}),
|
|
258
|
+
);
|
|
259
|
+
},
|
|
260
|
+
},
|
|
261
|
+
},
|
|
164
262
|
redis: { host: '127.0.0.1', port: 6379 },
|
|
263
|
+
auth: {
|
|
264
|
+
jwtSecretKey: process.env.JWT_SECRET || 'your-secret-key',
|
|
265
|
+
},
|
|
165
266
|
});
|
|
166
267
|
|
|
167
268
|
await app.start();
|
|
269
|
+
console.log('WebSocket server running at ws://localhost:3000/ws');
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
#### Using Rooms
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// Client joins a room (built-in system controller)
|
|
276
|
+
// Send from client: { type: 'system', action: 'joinRoom', data: { roomName: 'general', username: 'Alice' } }
|
|
277
|
+
|
|
278
|
+
// Server-side: Broadcast to room members
|
|
279
|
+
const roomClients = app.websocket.server.rooms.get('general');
|
|
280
|
+
roomClients?.forEach(clientId => {
|
|
281
|
+
const client = app.websocket.server.clientManager.getClient({ clientId });
|
|
282
|
+
if (client?.ws) {
|
|
283
|
+
app.websocket.server.sendClientMessage(client.ws, {
|
|
284
|
+
type: 'room',
|
|
285
|
+
action: 'message',
|
|
286
|
+
data: { text: 'Room-specific announcement' },
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
```
|
|
168
291
|
|
|
169
|
-
|
|
170
|
-
app.websocket.server.onConnection(client => {
|
|
171
|
-
console.log('Client connected:', client.id);
|
|
292
|
+
#### Using WebSocket Service
|
|
172
293
|
|
|
173
|
-
|
|
294
|
+
Simplified API for common operations:
|
|
174
295
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
296
|
+
```typescript
|
|
297
|
+
import { WebSocketService } from '@scpxl/nodejs-framework/websocket';
|
|
298
|
+
|
|
299
|
+
const wsService = new WebSocketService({
|
|
300
|
+
webSocketServer: app.websocket.server,
|
|
301
|
+
redisInstance: app.redis.instance,
|
|
302
|
+
workerId: String(process.pid),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Broadcast to all clients
|
|
306
|
+
await wsService.broadcast({
|
|
307
|
+
type: 'notification',
|
|
308
|
+
action: 'alert',
|
|
309
|
+
data: { message: 'New features available!' },
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Send to specific rooms
|
|
313
|
+
await wsService.sendToRooms(['vip', 'premium'], {
|
|
314
|
+
type: 'offer',
|
|
315
|
+
action: 'new',
|
|
316
|
+
data: { discount: 20 },
|
|
179
317
|
});
|
|
318
|
+
|
|
319
|
+
// Convenience methods
|
|
320
|
+
await wsService.sendUserMessage('profileUpdated', { userId: 123 });
|
|
321
|
+
await wsService.sendSystemMessage('maintenance', { minutes: 5 });
|
|
180
322
|
```
|
|
181
323
|
|
|
324
|
+
See the [WebSocket Guide](./docs/guides/websocket.md) for complete documentation.
|
|
325
|
+
|
|
182
326
|
---
|
|
183
327
|
|
|
184
328
|
## 📚 Documentation
|
|
@@ -212,6 +356,27 @@ The framework is built around three main application types:
|
|
|
212
356
|
| **API Requester** | HTTP client wrapper (migrated to native `fetch`) | `@scpxl/nodejs-framework/api-requester` |
|
|
213
357
|
| **Command** | CLI command framework | `@scpxl/nodejs-framework/command` |
|
|
214
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
|
|
215
380
|
|
|
216
381
|
### Key Patterns
|
|
217
382
|
|
|
@@ -299,15 +464,15 @@ A full-stack example with:
|
|
|
299
464
|
**Run the example:**
|
|
300
465
|
|
|
301
466
|
```bash
|
|
302
|
-
# Install dependencies
|
|
303
|
-
npm
|
|
467
|
+
# Install dependencies (one-time setup)
|
|
468
|
+
cd examples/hello-world/backend && npm install
|
|
469
|
+
cd ../frontend && npm install
|
|
304
470
|
|
|
305
|
-
# Run backend
|
|
306
|
-
npm run example
|
|
471
|
+
# Run backend
|
|
472
|
+
npm run example --example=hello-world/backend
|
|
307
473
|
|
|
308
|
-
# Or run
|
|
309
|
-
npm run example
|
|
310
|
-
npm run example:hello-world:frontend
|
|
474
|
+
# Or run frontend (in another terminal)
|
|
475
|
+
npm run example --example=hello-world/frontend
|
|
311
476
|
```
|
|
312
477
|
|
|
313
478
|
Then open http://localhost:5173 to see the app.
|
|
@@ -317,17 +482,13 @@ Then open http://localhost:5173 to see the app.
|
|
|
317
482
|
Demonstrates the command framework with examples:
|
|
318
483
|
|
|
319
484
|
```bash
|
|
320
|
-
# Install dependencies
|
|
321
|
-
npm
|
|
322
|
-
|
|
323
|
-
# Run hello command
|
|
324
|
-
npm run example:commands:hello
|
|
485
|
+
# Install dependencies (one-time setup)
|
|
486
|
+
cd examples/commands && npm install
|
|
325
487
|
|
|
326
|
-
# Run
|
|
327
|
-
npm run example
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
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
|
|
331
492
|
```
|
|
332
493
|
|
|
333
494
|
See [examples/README.md](examples/README.md) for more details.
|
|
@@ -336,6 +497,51 @@ See [examples/README.md](examples/README.md) for more details.
|
|
|
336
497
|
|
|
337
498
|
## 🛠️ Development
|
|
338
499
|
|
|
500
|
+
### CLI (`pxl`)
|
|
501
|
+
|
|
502
|
+
The framework now ships with a bundled CLI executable exposed as `pxl` when the package is installed.
|
|
503
|
+
|
|
504
|
+
Current capabilities:
|
|
505
|
+
|
|
506
|
+
- `pxl --version` / `pxl -v` / `pxl version` – Print framework version
|
|
507
|
+
- `pxl info` (or just `pxl`) – Display banner + roadmap
|
|
508
|
+
|
|
509
|
+
Planned subcommands (roadmap):
|
|
510
|
+
|
|
511
|
+
- `pxl doctor` – Environment diagnostics (Node version, dependency checks, Redis/Postgres availability)
|
|
512
|
+
- `pxl generate` – Scaffolding for applications, routes, commands, processors
|
|
513
|
+
- `pxl analyze` – Project inspection (unused files, dependency graph summary)
|
|
514
|
+
|
|
515
|
+
Usage examples:
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
# Show version
|
|
519
|
+
pxl --version
|
|
520
|
+
|
|
521
|
+
# Show framework banner and roadmap
|
|
522
|
+
pxl info
|
|
523
|
+
|
|
524
|
+
# (Future) Run doctor diagnostics
|
|
525
|
+
pxl doctor
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
Development Note:
|
|
529
|
+
|
|
530
|
+
When iterating locally, rebuild after CLI changes:
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
npm run build && pxl info
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
To test unpublished changes in another project via yalc:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
npm run build:local
|
|
540
|
+
yalc add @scpxl/nodejs-framework
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
For contributions adding new subcommands, implement them in `src/cli/` and register via the yargs builder in `src/cli/index.ts`.
|
|
544
|
+
|
|
339
545
|
### Build Commands
|
|
340
546
|
|
|
341
547
|
```bash
|
|
@@ -380,6 +586,38 @@ npm run test:ui
|
|
|
380
586
|
|
|
381
587
|
The framework maintains **80% code coverage** across all metrics (lines, branches, functions, statements) as enforced by Vitest thresholds.
|
|
382
588
|
|
|
589
|
+
### Framework Status Report
|
|
590
|
+
|
|
591
|
+
Generate a cross-platform project health snapshot (dependency counts, git status, directory sizes, large packages, outdated/age metrics):
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
npm run status
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
Optional flags:
|
|
598
|
+
|
|
599
|
+
```bash
|
|
600
|
+
npm run status -- --include-cache # Include .turbo/ and .next/cache directories
|
|
601
|
+
npm run status -- --exclude "coverage,fixtures,**/*.snap" # Additional exclude globs (comma-separated)
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
What it does:
|
|
605
|
+
|
|
606
|
+
- Collects repo & package metadata (version, scripts, dependency counts)
|
|
607
|
+
- Summarizes current git branch, last commit, and pending change counts
|
|
608
|
+
- Computes sizes for `src`, `dist`, and `node_modules` using fast native traversal (`fast-folder-size`) with a JS fallback, filtering via `.gitignore` / `.npmignore` plus exclusions
|
|
609
|
+
- Lists the largest packages in `node_modules` (top 8 by size)
|
|
610
|
+
- Ranks outdated dependencies by publish age and major version lag (uses `npm outdated` / `npm view`)
|
|
611
|
+
- Provides age distribution stats for installed top-level packages
|
|
612
|
+
|
|
613
|
+
Exclusions & Ignore Behavior:
|
|
614
|
+
|
|
615
|
+
- Always respects patterns found in `.gitignore` and `.npmignore` (except always keeps top-level `dist` & `node_modules`)
|
|
616
|
+
- Built-in default excludes: `coverage/`, `fixtures/`, and `**/*.snap`
|
|
617
|
+
- Skips heavy build caches (`.turbo`, `.next/cache`) unless `--include-cache` is passed
|
|
618
|
+
|
|
619
|
+
Return codes: exits with non-zero only on unexpected internal errors; missing npm or network failures simply degrade sections gracefully.
|
|
620
|
+
|
|
383
621
|
### Local Development with Yalc
|
|
384
622
|
|
|
385
623
|
For testing changes in consuming applications:
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface ApiRequestConfig extends Omit<RequestInit, 'method' | 'body' | 'headers'> {
|
|
2
|
+
headers?: Record<string, string | undefined>;
|
|
3
|
+
params?: Record<string, string | number | boolean | null | undefined>;
|
|
4
|
+
responseType?: 'json' | 'text';
|
|
5
|
+
}
|
|
6
|
+
export interface ApiResponse<T> {
|
|
7
|
+
data: T;
|
|
8
|
+
status: number;
|
|
9
|
+
statusText: string;
|
|
10
|
+
headers: Record<string, string>;
|
|
11
|
+
}
|
|
12
|
+
export default class ApiRequester {
|
|
13
|
+
private readonly baseURL;
|
|
14
|
+
private readonly defaultHeaders;
|
|
15
|
+
constructor(baseURL: string, headers?: Record<string, string>);
|
|
16
|
+
get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined>;
|
|
17
|
+
post<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined>;
|
|
18
|
+
put<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined>;
|
|
19
|
+
delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined>;
|
|
20
|
+
private request;
|
|
21
|
+
private mergeHeaders;
|
|
22
|
+
private prepareBody;
|
|
23
|
+
private shouldSerializeAsJson;
|
|
24
|
+
private headersToRecord;
|
|
25
|
+
private buildUrl;
|
|
26
|
+
private isAbsoluteUrl;
|
|
27
|
+
private combineWithBase;
|
|
28
|
+
private parseResponse;
|
|
29
|
+
private createHttpError;
|
|
30
|
+
private handleError;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=api-requester.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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 :
|
|
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": ["
|
|
5
|
-
"mappings": ";;
|
|
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
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api-requester/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC7D,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { type DatabaseInstance, DatabaseManager } from '../database/index.js';
|
|
2
|
+
import QueueManager from '../queue/manager.js';
|
|
3
|
+
import RedisManager from '../redis/manager.js';
|
|
4
|
+
import type { ApplicationConfig, ApplicationStopInstanceOptions } from './base-application.interface.js';
|
|
5
|
+
import type RedisInstance from '../redis/instance.js';
|
|
6
|
+
import CacheManager from '../cache/manager.js';
|
|
7
|
+
import EventManager from '../event/manager.js';
|
|
8
|
+
import type { PerformanceMonitor } from '../performance/performance-monitor.js';
|
|
9
|
+
import { LifecycleManager, ShutdownController } from '../lifecycle/index.js';
|
|
10
|
+
export type { ApplicationConfig } from './base-application.interface.js';
|
|
11
|
+
export default abstract class BaseApplication {
|
|
12
|
+
/** Unique instance ID */
|
|
13
|
+
uniqueInstanceId: string;
|
|
14
|
+
/** Application start time */
|
|
15
|
+
protected startTime: number;
|
|
16
|
+
/** Shutdown timeout (30 seconds) */
|
|
17
|
+
protected shutdownTimeout: number;
|
|
18
|
+
/** Cache for application version to avoid repeated imports */
|
|
19
|
+
private static applicationVersionCache;
|
|
20
|
+
/** Cluster worker ID */
|
|
21
|
+
protected workerId: number | null;
|
|
22
|
+
/** Application config */
|
|
23
|
+
protected config: ApplicationConfig;
|
|
24
|
+
/** Application version */
|
|
25
|
+
protected applicationVersion?: string;
|
|
26
|
+
/** Redis manager */
|
|
27
|
+
redisManager: RedisManager;
|
|
28
|
+
/** Cache manager */
|
|
29
|
+
cacheManager: CacheManager;
|
|
30
|
+
/** Database manager */
|
|
31
|
+
databaseManager?: DatabaseManager;
|
|
32
|
+
/** Queue manager */
|
|
33
|
+
queueManager?: QueueManager;
|
|
34
|
+
/** Event manager */
|
|
35
|
+
eventManager?: EventManager;
|
|
36
|
+
/** Performance monitor */
|
|
37
|
+
performanceMonitor?: PerformanceMonitor;
|
|
38
|
+
/** Lifecycle manager */
|
|
39
|
+
lifecycle: LifecycleManager;
|
|
40
|
+
/** Shutdown controller */
|
|
41
|
+
shutdownController: ShutdownController;
|
|
42
|
+
get Name(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Application constructor
|
|
45
|
+
*/
|
|
46
|
+
constructor(config: ApplicationConfig);
|
|
47
|
+
/**
|
|
48
|
+
* Get application version
|
|
49
|
+
*/
|
|
50
|
+
getApplicationVersion(): Promise<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Start application
|
|
53
|
+
*/
|
|
54
|
+
start(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Before application start
|
|
57
|
+
*/
|
|
58
|
+
private onBeforeStart;
|
|
59
|
+
/**
|
|
60
|
+
* Application started event
|
|
61
|
+
*/
|
|
62
|
+
protected onStarted({ startupTime: _startupTime }: {
|
|
63
|
+
startupTime: number;
|
|
64
|
+
}): void;
|
|
65
|
+
/**
|
|
66
|
+
* Application stopped event
|
|
67
|
+
*/
|
|
68
|
+
protected onStopped({ runtime: _runtime }: {
|
|
69
|
+
runtime: number;
|
|
70
|
+
}): void;
|
|
71
|
+
/**
|
|
72
|
+
* Start application instance
|
|
73
|
+
*/
|
|
74
|
+
private startInstance;
|
|
75
|
+
protected abstract startHandler({ redisInstance, databaseInstance, queueManager, eventManager, }: {
|
|
76
|
+
redisInstance: RedisInstance;
|
|
77
|
+
databaseInstance?: DatabaseInstance | null;
|
|
78
|
+
queueManager: QueueManager;
|
|
79
|
+
eventManager?: EventManager | null;
|
|
80
|
+
}): Promise<void>;
|
|
81
|
+
protected abstract stopCallback(): void;
|
|
82
|
+
/**
|
|
83
|
+
* Set up global error handlers
|
|
84
|
+
*/
|
|
85
|
+
/**
|
|
86
|
+
* Initialize performance monitor
|
|
87
|
+
*/
|
|
88
|
+
private setupGlobalErrorHandlers;
|
|
89
|
+
/**
|
|
90
|
+
* Register shutdown hooks for proper cleanup
|
|
91
|
+
*/
|
|
92
|
+
private registerShutdownHooks;
|
|
93
|
+
/**
|
|
94
|
+
* Initiate graceful shutdown
|
|
95
|
+
*/
|
|
96
|
+
private initiateGracefulShutdown;
|
|
97
|
+
/**
|
|
98
|
+
* Stop application using lifecycle manager
|
|
99
|
+
*/
|
|
100
|
+
stop({ onStopped }?: ApplicationStopInstanceOptions): Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Finalize exit: during tests, suppress actual process exit to avoid failing vitest runs.
|
|
103
|
+
*/
|
|
104
|
+
private finalizeExit;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=base-application.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-application.d.ts","sourceRoot":"","sources":["../../src/application/base-application.ts"],"names":[],"mappings":"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"}
|