@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.
Files changed (326) hide show
  1. package/README.md +264 -26
  2. package/dist/api-requester/api-requester.d.ts +32 -0
  3. package/dist/api-requester/api-requester.d.ts.map +1 -0
  4. package/dist/api-requester/api-requester.js +2 -1
  5. package/dist/api-requester/api-requester.js.map +2 -2
  6. package/dist/api-requester/index.d.ts +3 -0
  7. package/dist/api-requester/index.d.ts.map +1 -0
  8. package/dist/application/base-application.d.ts +106 -0
  9. package/dist/application/base-application.d.ts.map +1 -0
  10. package/dist/application/base-application.interface.d.ts +162 -0
  11. package/dist/application/base-application.interface.d.ts.map +1 -0
  12. package/dist/application/base-application.js +7 -3
  13. package/dist/application/base-application.js.map +2 -2
  14. package/dist/application/command-application.d.ts +18 -0
  15. package/dist/application/command-application.d.ts.map +1 -0
  16. package/dist/application/command-application.interface.d.ts +26 -0
  17. package/dist/application/command-application.interface.d.ts.map +1 -0
  18. package/dist/application/index.d.ts +5 -0
  19. package/dist/application/index.d.ts.map +1 -0
  20. package/dist/application/web-application.d.ts +43 -0
  21. package/dist/application/web-application.d.ts.map +1 -0
  22. package/dist/application/web-application.interface.d.ts +21 -0
  23. package/dist/application/web-application.interface.d.ts.map +1 -0
  24. package/dist/application/web-application.js +1 -0
  25. package/dist/application/web-application.js.map +2 -2
  26. package/dist/auth/index.d.ts +2 -0
  27. package/dist/auth/index.d.ts.map +1 -0
  28. package/dist/auth/jwt.d.ts +25 -0
  29. package/dist/auth/jwt.d.ts.map +1 -0
  30. package/dist/cache/index.d.ts +2 -0
  31. package/dist/cache/index.d.ts.map +1 -0
  32. package/dist/cache/manager.d.ts +107 -0
  33. package/dist/cache/manager.d.ts.map +1 -0
  34. package/dist/cache/manager.js +2 -1
  35. package/dist/cache/manager.js.map +2 -2
  36. package/dist/cli/index.d.ts +2 -0
  37. package/dist/cli/index.d.ts.map +1 -0
  38. package/dist/cli/index.js +12591 -0
  39. package/dist/cli/index.js.map +7 -0
  40. package/dist/cluster/cluster-manager.d.ts +18 -0
  41. package/dist/cluster/cluster-manager.d.ts.map +1 -0
  42. package/dist/cluster/cluster-manager.interface.d.ts +23 -0
  43. package/dist/cluster/cluster-manager.interface.d.ts.map +1 -0
  44. package/dist/cluster/cluster-manager.js +45 -8
  45. package/dist/cluster/cluster-manager.js.map +2 -2
  46. package/dist/cluster/index.d.ts +2 -0
  47. package/dist/cluster/index.d.ts.map +1 -0
  48. package/dist/command/command-manager.d.ts +19 -0
  49. package/dist/command/command-manager.d.ts.map +1 -0
  50. package/dist/command/command.d.ts +27 -0
  51. package/dist/command/command.d.ts.map +1 -0
  52. package/dist/command/command.interface.d.ts +11 -0
  53. package/dist/command/command.interface.d.ts.map +1 -0
  54. package/dist/command/index.d.ts +3 -0
  55. package/dist/command/index.d.ts.map +1 -0
  56. package/dist/config/env.d.ts +11 -0
  57. package/dist/config/env.d.ts.map +1 -0
  58. package/dist/config/index.d.ts +3 -0
  59. package/dist/config/index.d.ts.map +1 -0
  60. package/dist/config/schema.d.ts +432 -0
  61. package/dist/config/schema.d.ts.map +1 -0
  62. package/dist/database/dynamic-entity-form-decorators.d.ts +31 -0
  63. package/dist/database/dynamic-entity-form-decorators.d.ts.map +1 -0
  64. package/dist/database/dynamic-entity-form-decorators.js.map +1 -1
  65. package/dist/database/dynamic-entity.d.ts +18 -0
  66. package/dist/database/dynamic-entity.d.ts.map +1 -0
  67. package/dist/database/dynamic-entity.js +11 -1
  68. package/dist/database/dynamic-entity.js.map +2 -2
  69. package/dist/database/index.d.ts +5 -0
  70. package/dist/database/index.d.ts.map +1 -0
  71. package/dist/database/instance.d.ts +36 -0
  72. package/dist/database/instance.d.ts.map +1 -0
  73. package/dist/database/instance.interface.d.ts +5 -0
  74. package/dist/database/instance.interface.d.ts.map +1 -0
  75. package/dist/database/manager.d.ts +27 -0
  76. package/dist/database/manager.d.ts.map +1 -0
  77. package/dist/database/manager.interface.d.ts +18 -0
  78. package/dist/database/manager.interface.d.ts.map +1 -0
  79. package/dist/database/manager.js +3 -2
  80. package/dist/database/manager.js.map +2 -2
  81. package/dist/error/error-reporter.d.ts +109 -0
  82. package/dist/error/error-reporter.d.ts.map +1 -0
  83. package/dist/error/error-reporter.js +32 -29
  84. package/dist/error/error-reporter.js.map +2 -2
  85. package/dist/error/error.interface.d.ts +126 -0
  86. package/dist/error/error.interface.d.ts.map +1 -0
  87. package/dist/error/framework-errors.d.ts +113 -0
  88. package/dist/error/framework-errors.d.ts.map +1 -0
  89. package/dist/error/index.d.ts +6 -0
  90. package/dist/error/index.d.ts.map +1 -0
  91. package/dist/error/index.js +3 -2
  92. package/dist/error/index.js.map +2 -2
  93. package/dist/event/controller/base.d.ts +23 -0
  94. package/dist/event/controller/base.d.ts.map +1 -0
  95. package/dist/event/controller/base.interface.d.ts +11 -0
  96. package/dist/event/controller/base.interface.d.ts.map +1 -0
  97. package/dist/event/controller/base.js +2 -1
  98. package/dist/event/controller/base.js.map +2 -2
  99. package/dist/event/index.d.ts +5 -0
  100. package/dist/event/index.d.ts.map +1 -0
  101. package/dist/event/manager.d.ts +21 -0
  102. package/dist/event/manager.d.ts.map +1 -0
  103. package/dist/event/manager.interface.d.ts +137 -0
  104. package/dist/event/manager.interface.d.ts.map +1 -0
  105. package/dist/event/manager.js +5 -4
  106. package/dist/event/manager.js.map +2 -2
  107. package/dist/index.d.ts +22 -0
  108. package/dist/index.d.ts.map +1 -0
  109. package/dist/lifecycle/exit.d.ts +11 -0
  110. package/dist/lifecycle/exit.d.ts.map +1 -0
  111. package/dist/lifecycle/exit.js.map +2 -2
  112. package/dist/lifecycle/index.d.ts +7 -0
  113. package/dist/lifecycle/index.d.ts.map +1 -0
  114. package/dist/lifecycle/lifecycle-manager.d.ts +66 -0
  115. package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
  116. package/dist/lifecycle/lifecycle-manager.js +6 -11
  117. package/dist/lifecycle/lifecycle-manager.js.map +2 -2
  118. package/dist/lifecycle/shutdown-controller.d.ts +15 -0
  119. package/dist/lifecycle/shutdown-controller.d.ts.map +1 -0
  120. package/dist/lifecycle/types.d.ts +28 -0
  121. package/dist/lifecycle/types.d.ts.map +1 -0
  122. package/dist/logger/index.d.ts +2 -0
  123. package/dist/logger/index.d.ts.map +1 -0
  124. package/dist/logger/logger.d.ts +59 -0
  125. package/dist/logger/logger.d.ts.map +1 -0
  126. package/dist/logger/logger.interface.d.ts +2 -0
  127. package/dist/logger/logger.interface.d.ts.map +1 -0
  128. package/dist/logger/logger.js +11 -3
  129. package/dist/logger/logger.js.map +2 -2
  130. package/dist/performance/cache-performance.d.ts +64 -0
  131. package/dist/performance/cache-performance.d.ts.map +1 -0
  132. package/dist/performance/database-performance.d.ts +40 -0
  133. package/dist/performance/database-performance.d.ts.map +1 -0
  134. package/dist/performance/index.d.ts +8 -0
  135. package/dist/performance/index.d.ts.map +1 -0
  136. package/dist/performance/performance-monitor.d.ts +68 -0
  137. package/dist/performance/performance-monitor.d.ts.map +1 -0
  138. package/dist/performance/performance-monitor.js +10 -3
  139. package/dist/performance/performance-monitor.js.map +2 -2
  140. package/dist/performance/performance-monitor.plugin.d.ts +24 -0
  141. package/dist/performance/performance-monitor.plugin.d.ts.map +1 -0
  142. package/dist/performance/queue-performance.d.ts +46 -0
  143. package/dist/performance/queue-performance.d.ts.map +1 -0
  144. package/dist/performance/webserver-performance.d.ts +69 -0
  145. package/dist/performance/webserver-performance.d.ts.map +1 -0
  146. package/dist/performance/websocket-performance.d.ts +44 -0
  147. package/dist/performance/websocket-performance.d.ts.map +1 -0
  148. package/dist/queue/index.d.ts +6 -0
  149. package/dist/queue/index.d.ts.map +1 -0
  150. package/dist/queue/index.interface.d.ts +10 -0
  151. package/dist/queue/index.interface.d.ts.map +1 -0
  152. package/dist/queue/job.interface.d.ts +43 -0
  153. package/dist/queue/job.interface.d.ts.map +1 -0
  154. package/dist/queue/manager.d.ts +44 -0
  155. package/dist/queue/manager.d.ts.map +1 -0
  156. package/dist/queue/manager.interface.d.ts +18 -0
  157. package/dist/queue/manager.interface.d.ts.map +1 -0
  158. package/dist/queue/processor/base.d.ts +29 -0
  159. package/dist/queue/processor/base.d.ts.map +1 -0
  160. package/dist/queue/processor/base.js +2 -1
  161. package/dist/queue/processor/base.js.map +2 -2
  162. package/dist/queue/processor/processor.interface.d.ts +16 -0
  163. package/dist/queue/processor/processor.interface.d.ts.map +1 -0
  164. package/dist/queue/worker.d.ts +14 -0
  165. package/dist/queue/worker.d.ts.map +1 -0
  166. package/dist/queue/worker.interface.d.ts +13 -0
  167. package/dist/queue/worker.interface.d.ts.map +1 -0
  168. package/dist/redis/index.d.ts +3 -0
  169. package/dist/redis/index.d.ts.map +1 -0
  170. package/dist/redis/instance.d.ts +32 -0
  171. package/dist/redis/instance.d.ts.map +1 -0
  172. package/dist/redis/instance.interface.d.ts +9 -0
  173. package/dist/redis/instance.interface.d.ts.map +1 -0
  174. package/dist/redis/manager.d.ts +15 -0
  175. package/dist/redis/manager.d.ts.map +1 -0
  176. package/dist/redis/manager.interface.d.ts +8 -0
  177. package/dist/redis/manager.interface.d.ts.map +1 -0
  178. package/dist/redis/manager.js +16 -16
  179. package/dist/redis/manager.js.map +2 -2
  180. package/dist/request-context/index.d.ts +3 -0
  181. package/dist/request-context/index.d.ts.map +1 -0
  182. package/dist/request-context/request-context.d.ts +108 -0
  183. package/dist/request-context/request-context.d.ts.map +1 -0
  184. package/dist/request-context/request-context.interface.d.ts +46 -0
  185. package/dist/request-context/request-context.interface.d.ts.map +1 -0
  186. package/dist/schemas/common.d.ts +197 -0
  187. package/dist/schemas/common.d.ts.map +1 -0
  188. package/dist/schemas/common.js +108 -0
  189. package/dist/schemas/common.js.map +7 -0
  190. package/dist/schemas/index.d.ts +6 -0
  191. package/dist/schemas/index.d.ts.map +1 -0
  192. package/dist/schemas/index.js +2 -0
  193. package/dist/schemas/index.js.map +7 -0
  194. package/dist/services/aws/index.d.ts +2 -0
  195. package/dist/services/aws/index.d.ts.map +1 -0
  196. package/dist/services/aws/s3.d.ts +54 -0
  197. package/dist/services/aws/s3.d.ts.map +1 -0
  198. package/dist/services/aws/s3.interface.d.ts +14 -0
  199. package/dist/services/aws/s3.interface.d.ts.map +1 -0
  200. package/dist/services/index.d.ts +2 -0
  201. package/dist/services/index.d.ts.map +1 -0
  202. package/dist/util/file.d.ts +58 -0
  203. package/dist/util/file.d.ts.map +1 -0
  204. package/dist/util/helper.d.ts +51 -0
  205. package/dist/util/helper.d.ts.map +1 -0
  206. package/dist/util/helper.js +72 -10
  207. package/dist/util/helper.js.map +2 -2
  208. package/dist/util/image.d.ts +12 -0
  209. package/dist/util/image.d.ts.map +1 -0
  210. package/dist/util/index.d.ts +11 -0
  211. package/dist/util/index.d.ts.map +1 -0
  212. package/dist/util/loader.d.ts +21 -0
  213. package/dist/util/loader.d.ts.map +1 -0
  214. package/dist/util/loader.js +5 -2
  215. package/dist/util/loader.js.map +2 -2
  216. package/dist/util/num.d.ts +13 -0
  217. package/dist/util/num.d.ts.map +1 -0
  218. package/dist/util/os.d.ts +6 -0
  219. package/dist/util/os.d.ts.map +1 -0
  220. package/dist/util/str.d.ts +39 -0
  221. package/dist/util/str.d.ts.map +1 -0
  222. package/dist/util/time.d.ts +19 -0
  223. package/dist/util/time.d.ts.map +1 -0
  224. package/dist/util/time.interface.d.ts +12 -0
  225. package/dist/util/time.interface.d.ts.map +1 -0
  226. package/dist/util/timing.d.ts +36 -0
  227. package/dist/util/timing.d.ts.map +1 -0
  228. package/dist/util/timing.interface.d.ts +47 -0
  229. package/dist/util/timing.interface.d.ts.map +1 -0
  230. package/dist/util/url.d.ts +7 -0
  231. package/dist/util/url.d.ts.map +1 -0
  232. package/dist/webserver/controller/auth-middleware.d.ts +21 -0
  233. package/dist/webserver/controller/auth-middleware.d.ts.map +1 -0
  234. package/dist/webserver/controller/base.d.ts +41 -0
  235. package/dist/webserver/controller/base.d.ts.map +1 -0
  236. package/dist/webserver/controller/base.interface.d.ts +50 -0
  237. package/dist/webserver/controller/base.interface.d.ts.map +1 -0
  238. package/dist/webserver/controller/base.js +4 -4
  239. package/dist/webserver/controller/base.js.map +2 -2
  240. package/dist/webserver/controller/entity.d.ts +94 -0
  241. package/dist/webserver/controller/entity.d.ts.map +1 -0
  242. package/dist/webserver/controller/entity.js.map +2 -2
  243. package/dist/webserver/controller/example-auth.d.ts +12 -0
  244. package/dist/webserver/controller/example-auth.d.ts.map +1 -0
  245. package/dist/webserver/controller/health.d.ts +13 -0
  246. package/dist/webserver/controller/health.d.ts.map +1 -0
  247. package/dist/webserver/controller/health.js +0 -14
  248. package/dist/webserver/controller/health.js.map +2 -2
  249. package/dist/webserver/define-action.d.ts +26 -0
  250. package/dist/webserver/define-action.d.ts.map +1 -0
  251. package/dist/webserver/define-action.js +16 -0
  252. package/dist/webserver/define-action.js.map +7 -0
  253. package/dist/webserver/define-route.d.ts +53 -0
  254. package/dist/webserver/define-route.d.ts.map +1 -0
  255. package/dist/webserver/define-route.js +11 -6
  256. package/dist/webserver/define-route.js.map +2 -2
  257. package/dist/webserver/index.d.ts +14 -0
  258. package/dist/webserver/index.d.ts.map +1 -0
  259. package/dist/webserver/index.js +2 -0
  260. package/dist/webserver/index.js.map +2 -2
  261. package/dist/webserver/util.d.ts +10 -0
  262. package/dist/webserver/util.d.ts.map +1 -0
  263. package/dist/webserver/util.js +5 -2
  264. package/dist/webserver/util.js.map +2 -2
  265. package/dist/webserver/webserver.d.ts +93 -0
  266. package/dist/webserver/webserver.d.ts.map +1 -0
  267. package/dist/webserver/webserver.interface.d.ts +181 -0
  268. package/dist/webserver/webserver.interface.d.ts.map +1 -0
  269. package/dist/webserver/webserver.interface.js.map +1 -1
  270. package/dist/webserver/webserver.js +30 -33
  271. package/dist/webserver/webserver.js.map +2 -2
  272. package/dist/websocket/controller/client/base.d.ts +12 -0
  273. package/dist/websocket/controller/client/base.d.ts.map +1 -0
  274. package/dist/websocket/controller/client/base.interface.d.ts +12 -0
  275. package/dist/websocket/controller/client/base.interface.d.ts.map +1 -0
  276. package/dist/websocket/controller/server/base.d.ts +13 -0
  277. package/dist/websocket/controller/server/base.d.ts.map +1 -0
  278. package/dist/websocket/controller/server/base.interface.d.ts +13 -0
  279. package/dist/websocket/controller/server/base.interface.d.ts.map +1 -0
  280. package/dist/websocket/controllers/client/system.d.ts +6 -0
  281. package/dist/websocket/controllers/client/system.d.ts.map +1 -0
  282. package/dist/websocket/controllers/server/system.d.ts +7 -0
  283. package/dist/websocket/controllers/server/system.d.ts.map +1 -0
  284. package/dist/websocket/index.d.ts +9 -0
  285. package/dist/websocket/index.d.ts.map +1 -0
  286. package/dist/websocket/index.js +2 -0
  287. package/dist/websocket/index.js.map +2 -2
  288. package/dist/websocket/routes/client/system.d.ts +3 -0
  289. package/dist/websocket/routes/client/system.d.ts.map +1 -0
  290. package/dist/websocket/routes/server/system.d.ts +3 -0
  291. package/dist/websocket/routes/server/system.d.ts.map +1 -0
  292. package/dist/websocket/utils.d.ts +9 -0
  293. package/dist/websocket/utils.d.ts.map +1 -0
  294. package/dist/websocket/websocket-auth.d.ts +17 -0
  295. package/dist/websocket/websocket-auth.d.ts.map +1 -0
  296. package/dist/websocket/websocket-auth.js +46 -0
  297. package/dist/websocket/websocket-auth.js.map +7 -0
  298. package/dist/websocket/websocket-base.d.ts +19 -0
  299. package/dist/websocket/websocket-base.d.ts.map +1 -0
  300. package/dist/websocket/websocket-client-manager.d.ts +53 -0
  301. package/dist/websocket/websocket-client-manager.d.ts.map +1 -0
  302. package/dist/websocket/websocket-client-manager.interface.d.ts +8 -0
  303. package/dist/websocket/websocket-client-manager.interface.d.ts.map +1 -0
  304. package/dist/websocket/websocket-client-manager.js +6 -5
  305. package/dist/websocket/websocket-client-manager.js.map +2 -2
  306. package/dist/websocket/websocket-client.d.ts +64 -0
  307. package/dist/websocket/websocket-client.d.ts.map +1 -0
  308. package/dist/websocket/websocket-client.interface.d.ts +14 -0
  309. package/dist/websocket/websocket-client.interface.d.ts.map +1 -0
  310. package/dist/websocket/websocket-client.js +97 -3
  311. package/dist/websocket/websocket-client.js.map +2 -2
  312. package/dist/websocket/websocket-room-manager.d.ts +32 -0
  313. package/dist/websocket/websocket-room-manager.d.ts.map +1 -0
  314. package/dist/websocket/websocket-server.d.ts +102 -0
  315. package/dist/websocket/websocket-server.d.ts.map +1 -0
  316. package/dist/websocket/websocket-server.interface.d.ts +16 -0
  317. package/dist/websocket/websocket-server.interface.d.ts.map +1 -0
  318. package/dist/websocket/websocket-server.js +62 -50
  319. package/dist/websocket/websocket-server.js.map +2 -2
  320. package/dist/websocket/websocket-service.d.ts +44 -0
  321. package/dist/websocket/websocket-service.d.ts.map +1 -0
  322. package/dist/websocket/websocket.interface.d.ts +137 -0
  323. package/dist/websocket/websocket.interface.d.ts.map +1 -0
  324. package/dist/websocket/websocket.interface.js.map +2 -2
  325. package/package.json +21 -24
  326. 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: { enabled: true },
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
- // Handle WebSocket connections
170
- app.websocket.server.onConnection(client => {
171
- console.log('Client connected:', client.id);
292
+ #### Using WebSocket Service
172
293
 
173
- client.sendJSON({ type: 'welcome', message: 'Connected to chat server' });
294
+ Simplified API for common operations:
174
295
 
175
- client.on('message', data => {
176
- // Broadcast to all clients
177
- app.websocket.server.broadcast({ type: 'chat', data });
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 for examples (one-time setup)
303
- npm run example:install
467
+ # Install dependencies (one-time setup)
468
+ cd examples/hello-world/backend && npm install
469
+ cd ../frontend && npm install
304
470
 
305
- # Run backend + frontend together with hot-reload
306
- npm run example:hello-world
471
+ # Run backend
472
+ npm run example --example=hello-world/backend
307
473
 
308
- # Or run individually
309
- npm run example:hello-world:backend
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 run example:commands:install
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 database seed command
327
- npm run example:commands:seed
328
-
329
- # Run queue processing command
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 : String(error);
170
+ const reason = error instanceof Error ? error.message : safeSerializeError(error);
170
171
  throw new Error(`Failed to parse JSON response: ${reason}`);
171
172
  }
172
173
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/api-requester/api-requester.ts"],
4
- "sourcesContent": ["export interface ApiRequestConfig extends Omit<RequestInit, 'method' | 'body' | 'headers'> {\n headers?: Record<string, string | undefined>;\n params?: Record<string, string | number | boolean | null | undefined>;\n responseType?: 'json' | 'text';\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nclass ApiRequesterHttpError extends Error {\n public readonly status: number;\n public readonly statusText: string;\n public readonly data: unknown;\n public readonly headers: Record<string, string>;\n\n constructor(\n message: string,\n options: {\n status: number;\n statusText: string;\n data: unknown;\n headers: Record<string, string>;\n },\n ) {\n super(message);\n this.name = 'ApiRequesterHttpError';\n this.status = options.status;\n this.statusText = options.statusText;\n this.data = options.data;\n this.headers = options.headers;\n }\n}\n\nexport default class ApiRequester {\n private readonly baseURL: string;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(baseURL: string, headers: Record<string, string> = {}) {\n this.baseURL = baseURL;\n this.defaultHeaders = { ...headers };\n }\n\n public async get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('GET', url, undefined, config);\n }\n\n public async post<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('POST', url, data, config);\n }\n\n public async put<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('PUT', url, data, config);\n }\n\n public async delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('DELETE', url, undefined, config);\n }\n\n private async request<T>(\n method: string,\n url: string,\n data?: unknown,\n config?: ApiRequestConfig,\n ): Promise<ApiResponse<T> | undefined> {\n try {\n const { headers: configHeaders, params, responseType = 'json', ...init } = config ?? {};\n const finalUrl = this.buildUrl(url, params);\n const headers = this.mergeHeaders(configHeaders, data);\n const body = this.prepareBody(method, data);\n\n // fetch is a global in Node.js 18+\n // eslint-disable-next-line no-undef\n const response = await fetch(finalUrl, {\n ...init,\n method,\n headers,\n body,\n });\n\n if (!response.ok) {\n throw await this.createHttpError(response);\n }\n\n const parsed = await this.parseResponse<T>(response, responseType);\n\n return {\n data: parsed,\n status: response.status,\n statusText: response.statusText,\n headers: this.headersToRecord(response.headers),\n };\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private mergeHeaders(headers?: Record<string, string | undefined>, body?: unknown): Headers {\n const merged = new Headers();\n\n for (const [key, value] of Object.entries(this.defaultHeaders)) {\n if (value !== undefined) {\n merged.set(key, value);\n }\n }\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n merged.set(key, value);\n } else {\n merged.delete(key);\n }\n }\n }\n\n if (body !== undefined && this.shouldSerializeAsJson(body) && !merged.has('content-type')) {\n merged.set('content-type', 'application/json');\n }\n\n return merged;\n }\n\n private prepareBody(method: string, data?: unknown): BodyInit | null | undefined {\n if (method === 'GET' || method === 'HEAD' || data === undefined) {\n return undefined;\n }\n\n if (typeof data === 'string' || data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\n return data as BodyInit;\n }\n\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n return data;\n }\n\n if (typeof FormData !== 'undefined' && data instanceof FormData) {\n return data;\n }\n\n if (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) {\n return data;\n }\n\n if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) {\n return data;\n }\n\n return JSON.stringify(data);\n }\n\n private shouldSerializeAsJson(data: unknown): boolean {\n if (data === null) {\n return true;\n }\n\n if (Array.isArray(data)) {\n return true;\n }\n\n const primitiveTypes = ['string', 'number', 'boolean', 'bigint'];\n if (primitiveTypes.includes(typeof data)) {\n return false;\n }\n\n if (typeof data === 'object') {\n return !(\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data) ||\n (typeof Blob !== 'undefined' && data instanceof Blob) ||\n (typeof FormData !== 'undefined' && data instanceof FormData) ||\n (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) ||\n (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) ||\n (typeof Buffer !== 'undefined' && Buffer.isBuffer(data))\n );\n }\n\n return false;\n }\n\n private headersToRecord(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n // Safe: key comes from Headers iterator, not user input\n // eslint-disable-next-line security/detect-object-injection\n result[key] = value;\n });\n return result;\n }\n\n private buildUrl(path: string, params?: Record<string, string | number | boolean | null | undefined>): string {\n const absolute = this.isAbsoluteUrl(path) ? path : this.combineWithBase(path);\n if (!params) {\n return absolute;\n }\n\n const url = new URL(absolute);\n for (const [key, rawValue] of Object.entries(params)) {\n if (rawValue === undefined || rawValue === null) {\n continue;\n }\n url.searchParams.set(key, String(rawValue));\n }\n return url.toString();\n }\n\n private isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n }\n\n private combineWithBase(path: string): string {\n if (!this.baseURL) {\n return path;\n }\n\n const normalizedBase = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${normalizedBase}${normalizedPath}`;\n }\n\n private async parseResponse<T>(response: Response, responseType: 'json' | 'text'): Promise<T> {\n if (responseType === 'text') {\n return (await response.text()) as T;\n }\n\n const body = await response.text();\n if (!body) {\n return undefined as T;\n }\n\n try {\n return JSON.parse(body) as T;\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to parse JSON response: ${reason}`);\n }\n }\n\n private async createHttpError(response: Response): Promise<ApiRequesterHttpError> {\n const headers = this.headersToRecord(response.headers);\n let data: unknown = undefined;\n let message = `Request failed with status ${response.status}`;\n\n try {\n const text = await response.text();\n if (text) {\n const contentType = headers['content-type'] ?? '';\n if (contentType.includes('application/json')) {\n data = JSON.parse(text);\n if (data && typeof data === 'object' && 'message' in (data as Record<string, unknown>)) {\n const potentialMessage = (data as Record<string, unknown>).message;\n if (typeof potentialMessage === 'string') {\n message = potentialMessage;\n }\n }\n } else {\n data = text;\n message = text;\n }\n }\n } catch {\n // If parsing fails, use defaults set above\n data = undefined;\n message = `Request failed with status ${response.status}`;\n }\n\n return new ApiRequesterHttpError(message, {\n status: response.status,\n statusText: response.statusText,\n data,\n headers,\n });\n }\n\n private handleError(error: unknown): never {\n if (error instanceof ApiRequesterHttpError) {\n console.error('HTTP error:', {\n status: error.status,\n statusText: error.statusText,\n data: error.data,\n });\n } else {\n console.error('Unexpected error:', error);\n }\n\n throw error;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { safeSerializeError } from '../error/error-reporter.js';\n\nexport interface ApiRequestConfig extends Omit<RequestInit, 'method' | 'body' | 'headers'> {\n headers?: Record<string, string | undefined>;\n params?: Record<string, string | number | boolean | null | undefined>;\n responseType?: 'json' | 'text';\n}\n\nexport interface ApiResponse<T> {\n data: T;\n status: number;\n statusText: string;\n headers: Record<string, string>;\n}\n\nclass ApiRequesterHttpError extends Error {\n public readonly status: number;\n public readonly statusText: string;\n public readonly data: unknown;\n public readonly headers: Record<string, string>;\n\n constructor(\n message: string,\n options: {\n status: number;\n statusText: string;\n data: unknown;\n headers: Record<string, string>;\n },\n ) {\n super(message);\n this.name = 'ApiRequesterHttpError';\n this.status = options.status;\n this.statusText = options.statusText;\n this.data = options.data;\n this.headers = options.headers;\n }\n}\n\nexport default class ApiRequester {\n private readonly baseURL: string;\n private readonly defaultHeaders: Record<string, string>;\n\n constructor(baseURL: string, headers: Record<string, string> = {}) {\n this.baseURL = baseURL;\n this.defaultHeaders = { ...headers };\n }\n\n public async get<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('GET', url, undefined, config);\n }\n\n public async post<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('POST', url, data, config);\n }\n\n public async put<T, R>(url: string, data: T, config?: ApiRequestConfig): Promise<ApiResponse<R> | undefined> {\n return this.request<R>('PUT', url, data, config);\n }\n\n public async delete<T>(url: string, config?: ApiRequestConfig): Promise<ApiResponse<T> | undefined> {\n return this.request<T>('DELETE', url, undefined, config);\n }\n\n private async request<T>(\n method: string,\n url: string,\n data?: unknown,\n config?: ApiRequestConfig,\n ): Promise<ApiResponse<T> | undefined> {\n try {\n const { headers: configHeaders, params, responseType = 'json', ...init } = config ?? {};\n const finalUrl = this.buildUrl(url, params);\n const headers = this.mergeHeaders(configHeaders, data);\n const body = this.prepareBody(method, data);\n\n // fetch is a global in Node.js 18+\n // eslint-disable-next-line no-undef\n const response = await fetch(finalUrl, {\n ...init,\n method,\n headers,\n body,\n });\n\n if (!response.ok) {\n throw await this.createHttpError(response);\n }\n\n const parsed = await this.parseResponse<T>(response, responseType);\n\n return {\n data: parsed,\n status: response.status,\n statusText: response.statusText,\n headers: this.headersToRecord(response.headers),\n };\n } catch (error) {\n this.handleError(error);\n }\n }\n\n private mergeHeaders(headers?: Record<string, string | undefined>, body?: unknown): Headers {\n const merged = new Headers();\n\n for (const [key, value] of Object.entries(this.defaultHeaders)) {\n if (value !== undefined) {\n merged.set(key, value);\n }\n }\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value !== undefined) {\n merged.set(key, value);\n } else {\n merged.delete(key);\n }\n }\n }\n\n if (body !== undefined && this.shouldSerializeAsJson(body) && !merged.has('content-type')) {\n merged.set('content-type', 'application/json');\n }\n\n return merged;\n }\n\n private prepareBody(method: string, data?: unknown): BodyInit | null | undefined {\n if (method === 'GET' || method === 'HEAD' || data === undefined) {\n return undefined;\n }\n\n if (typeof data === 'string' || data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {\n return data as BodyInit;\n }\n\n if (typeof Blob !== 'undefined' && data instanceof Blob) {\n return data;\n }\n\n if (typeof FormData !== 'undefined' && data instanceof FormData) {\n return data;\n }\n\n if (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) {\n return data;\n }\n\n if (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) {\n return data;\n }\n\n return JSON.stringify(data);\n }\n\n private shouldSerializeAsJson(data: unknown): boolean {\n if (data === null) {\n return true;\n }\n\n if (Array.isArray(data)) {\n return true;\n }\n\n const primitiveTypes = ['string', 'number', 'boolean', 'bigint'];\n if (primitiveTypes.includes(typeof data)) {\n return false;\n }\n\n if (typeof data === 'object') {\n return !(\n data instanceof ArrayBuffer ||\n ArrayBuffer.isView(data) ||\n (typeof Blob !== 'undefined' && data instanceof Blob) ||\n (typeof FormData !== 'undefined' && data instanceof FormData) ||\n (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) ||\n (typeof ReadableStream !== 'undefined' && data instanceof ReadableStream) ||\n (typeof Buffer !== 'undefined' && Buffer.isBuffer(data))\n );\n }\n\n return false;\n }\n\n private headersToRecord(headers: Headers): Record<string, string> {\n const result: Record<string, string> = {};\n headers.forEach((value, key) => {\n // Safe: key comes from Headers iterator, not user input\n // eslint-disable-next-line security/detect-object-injection\n result[key] = value;\n });\n return result;\n }\n\n private buildUrl(path: string, params?: Record<string, string | number | boolean | null | undefined>): string {\n const absolute = this.isAbsoluteUrl(path) ? path : this.combineWithBase(path);\n if (!params) {\n return absolute;\n }\n\n const url = new URL(absolute);\n for (const [key, rawValue] of Object.entries(params)) {\n if (rawValue === undefined || rawValue === null) {\n continue;\n }\n url.searchParams.set(key, String(rawValue));\n }\n return url.toString();\n }\n\n private isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n }\n\n private combineWithBase(path: string): string {\n if (!this.baseURL) {\n return path;\n }\n\n const normalizedBase = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;\n const normalizedPath = path.startsWith('/') ? path : `/${path}`;\n return `${normalizedBase}${normalizedPath}`;\n }\n\n private async parseResponse<T>(response: Response, responseType: 'json' | 'text'): Promise<T> {\n if (responseType === 'text') {\n return (await response.text()) as T;\n }\n\n const body = await response.text();\n if (!body) {\n return undefined as T;\n }\n\n try {\n return JSON.parse(body) as T;\n } catch (error) {\n const reason = error instanceof Error ? error.message : safeSerializeError(error);\n throw new Error(`Failed to parse JSON response: ${reason}`);\n }\n }\n\n private async createHttpError(response: Response): Promise<ApiRequesterHttpError> {\n const headers = this.headersToRecord(response.headers);\n let data: unknown = undefined;\n let message = `Request failed with status ${response.status}`;\n\n try {\n const text = await response.text();\n if (text) {\n const contentType = headers['content-type'] ?? '';\n if (contentType.includes('application/json')) {\n data = JSON.parse(text);\n if (data && typeof data === 'object' && 'message' in (data as Record<string, unknown>)) {\n const potentialMessage = (data as Record<string, unknown>).message;\n if (typeof potentialMessage === 'string') {\n message = potentialMessage;\n }\n }\n } else {\n data = text;\n message = text;\n }\n }\n } catch {\n // If parsing fails, use defaults set above\n data = undefined;\n message = `Request failed with status ${response.status}`;\n }\n\n return new ApiRequesterHttpError(message, {\n status: response.status,\n statusText: response.statusText,\n data,\n headers,\n });\n }\n\n private handleError(error: unknown): never {\n if (error instanceof ApiRequesterHttpError) {\n console.error('HTTP error:', {\n status: error.status,\n statusText: error.statusText,\n data: error.data,\n });\n } else {\n console.error('Unexpected error:', error);\n }\n\n throw error;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,3 @@
1
+ export { default as ApiRequester } from './api-requester.js';
2
+ export type { ApiRequestConfig, ApiResponse } from './api-requester.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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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"}