@engjts/nexus 0.1.7 → 0.1.9

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 (259) hide show
  1. package/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
  2. package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
  3. package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
  4. package/dist/advanced/playground/playground.d.ts +19 -0
  5. package/dist/advanced/playground/playground.d.ts.map +1 -1
  6. package/dist/advanced/playground/playground.js +70 -0
  7. package/dist/advanced/playground/playground.js.map +1 -1
  8. package/dist/advanced/playground/types.d.ts +20 -0
  9. package/dist/advanced/playground/types.d.ts.map +1 -1
  10. package/dist/core/application.d.ts +14 -0
  11. package/dist/core/application.d.ts.map +1 -1
  12. package/dist/core/application.js +173 -71
  13. package/dist/core/application.js.map +1 -1
  14. package/dist/core/context-pool.d.ts +2 -13
  15. package/dist/core/context-pool.d.ts.map +1 -1
  16. package/dist/core/context-pool.js +7 -45
  17. package/dist/core/context-pool.js.map +1 -1
  18. package/dist/core/context.d.ts +108 -5
  19. package/dist/core/context.d.ts.map +1 -1
  20. package/dist/core/context.js +449 -53
  21. package/dist/core/context.js.map +1 -1
  22. package/dist/core/index.d.ts +1 -0
  23. package/dist/core/index.d.ts.map +1 -1
  24. package/dist/core/index.js +9 -1
  25. package/dist/core/index.js.map +1 -1
  26. package/dist/core/middleware.d.ts +6 -0
  27. package/dist/core/middleware.d.ts.map +1 -1
  28. package/dist/core/middleware.js +83 -84
  29. package/dist/core/middleware.js.map +1 -1
  30. package/dist/core/performance/fast-json.d.ts +149 -0
  31. package/dist/core/performance/fast-json.d.ts.map +1 -0
  32. package/dist/core/performance/fast-json.js +473 -0
  33. package/dist/core/performance/fast-json.js.map +1 -0
  34. package/dist/core/router/file-router.d.ts +20 -7
  35. package/dist/core/router/file-router.d.ts.map +1 -1
  36. package/dist/core/router/file-router.js +41 -13
  37. package/dist/core/router/file-router.js.map +1 -1
  38. package/dist/core/router/index.d.ts +6 -0
  39. package/dist/core/router/index.d.ts.map +1 -1
  40. package/dist/core/router/index.js +33 -6
  41. package/dist/core/router/index.js.map +1 -1
  42. package/dist/core/router/radix-tree.d.ts +4 -1
  43. package/dist/core/router/radix-tree.d.ts.map +1 -1
  44. package/dist/core/router/radix-tree.js +7 -3
  45. package/dist/core/router/radix-tree.js.map +1 -1
  46. package/dist/core/serializer.d.ts +251 -0
  47. package/dist/core/serializer.d.ts.map +1 -0
  48. package/dist/core/serializer.js +290 -0
  49. package/dist/core/serializer.js.map +1 -0
  50. package/dist/core/types.d.ts +39 -1
  51. package/dist/core/types.d.ts.map +1 -1
  52. package/dist/core/types.js.map +1 -1
  53. package/dist/index.d.ts +1 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +12 -2
  56. package/dist/index.js.map +1 -1
  57. package/package.json +3 -1
  58. package/documentation/01-getting-started.md +0 -240
  59. package/documentation/02-context.md +0 -335
  60. package/documentation/03-routing.md +0 -397
  61. package/documentation/04-middleware.md +0 -483
  62. package/documentation/05-validation.md +0 -514
  63. package/documentation/06-error-handling.md +0 -465
  64. package/documentation/07-performance.md +0 -364
  65. package/documentation/08-adapters.md +0 -470
  66. package/documentation/09-api-reference.md +0 -548
  67. package/documentation/10-examples.md +0 -582
  68. package/documentation/11-deployment.md +0 -477
  69. package/documentation/12-sentry.md +0 -620
  70. package/documentation/13-sentry-data-storage.md +0 -996
  71. package/documentation/14-sentry-data-reference.md +0 -457
  72. package/documentation/15-sentry-summary.md +0 -409
  73. package/documentation/16-alerts-system.md +0 -745
  74. package/documentation/17-alert-adapters.md +0 -696
  75. package/documentation/18-alerts-implementation-summary.md +0 -385
  76. package/documentation/19-class-based-routing.md +0 -840
  77. package/documentation/20-websocket-realtime.md +0 -813
  78. package/documentation/21-cache-system.md +0 -510
  79. package/documentation/22-job-queue.md +0 -772
  80. package/documentation/23-sentry-plugin.md +0 -551
  81. package/documentation/24-testing-utilities.md +0 -1287
  82. package/documentation/25-api-versioning.md +0 -533
  83. package/documentation/26-context-store.md +0 -607
  84. package/documentation/27-dependency-injection.md +0 -329
  85. package/documentation/28-lifecycle-hooks.md +0 -521
  86. package/documentation/29-package-structure.md +0 -196
  87. package/documentation/30-plugin-system.md +0 -414
  88. package/documentation/31-jwt-authentication.md +0 -597
  89. package/documentation/32-cli.md +0 -268
  90. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  91. package/documentation/ALERTS-INDEX.md +0 -330
  92. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  93. package/documentation/README.md +0 -178
  94. package/documentation/index.html +0 -34
  95. package/modern_framework_paper.md +0 -1870
  96. package/public/css/style.css +0 -87
  97. package/public/index.html +0 -34
  98. package/public/js/app.js +0 -27
  99. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  100. package/src/advanced/cache/MultiTierCache.ts +0 -194
  101. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  102. package/src/advanced/cache/index.ts +0 -5
  103. package/src/advanced/cache/types.ts +0 -40
  104. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  105. package/src/advanced/graphql/index.ts +0 -22
  106. package/src/advanced/graphql/server.ts +0 -252
  107. package/src/advanced/graphql/types.ts +0 -42
  108. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  109. package/src/advanced/jobs/JobQueue.ts +0 -556
  110. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  111. package/src/advanced/jobs/index.ts +0 -5
  112. package/src/advanced/jobs/types.ts +0 -70
  113. package/src/advanced/observability/APMManager.ts +0 -163
  114. package/src/advanced/observability/AlertManager.ts +0 -109
  115. package/src/advanced/observability/MetricRegistry.ts +0 -151
  116. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  117. package/src/advanced/observability/StructuredLogger.ts +0 -154
  118. package/src/advanced/observability/TracingManager.ts +0 -117
  119. package/src/advanced/observability/adapters.ts +0 -304
  120. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  121. package/src/advanced/observability/index.ts +0 -11
  122. package/src/advanced/observability/types.ts +0 -174
  123. package/src/advanced/playground/extractPathParams.ts +0 -6
  124. package/src/advanced/playground/generateFieldExample.ts +0 -31
  125. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1849
  126. package/src/advanced/playground/generateSummary.ts +0 -19
  127. package/src/advanced/playground/getTagFromPath.ts +0 -9
  128. package/src/advanced/playground/index.ts +0 -8
  129. package/src/advanced/playground/playground.ts +0 -170
  130. package/src/advanced/playground/types.ts +0 -20
  131. package/src/advanced/playground/zodToExample.ts +0 -16
  132. package/src/advanced/playground/zodToParams.ts +0 -15
  133. package/src/advanced/postman/buildAuth.ts +0 -31
  134. package/src/advanced/postman/buildBody.ts +0 -15
  135. package/src/advanced/postman/buildQueryParams.ts +0 -27
  136. package/src/advanced/postman/buildRequestItem.ts +0 -36
  137. package/src/advanced/postman/buildResponses.ts +0 -11
  138. package/src/advanced/postman/buildUrl.ts +0 -33
  139. package/src/advanced/postman/capitalize.ts +0 -4
  140. package/src/advanced/postman/generateCollection.ts +0 -59
  141. package/src/advanced/postman/generateEnvironment.ts +0 -34
  142. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  143. package/src/advanced/postman/generateFieldExample.ts +0 -45
  144. package/src/advanced/postman/generateName.ts +0 -20
  145. package/src/advanced/postman/generateUUID.ts +0 -11
  146. package/src/advanced/postman/getTagFromPath.ts +0 -10
  147. package/src/advanced/postman/index.ts +0 -28
  148. package/src/advanced/postman/postman.ts +0 -156
  149. package/src/advanced/postman/slugify.ts +0 -7
  150. package/src/advanced/postman/types.ts +0 -140
  151. package/src/advanced/realtime/index.ts +0 -18
  152. package/src/advanced/realtime/websocket.ts +0 -231
  153. package/src/advanced/sentry/index.ts +0 -1236
  154. package/src/advanced/sentry/types.ts +0 -355
  155. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  156. package/src/advanced/static/generateETag.ts +0 -7
  157. package/src/advanced/static/getMimeType.ts +0 -9
  158. package/src/advanced/static/index.ts +0 -32
  159. package/src/advanced/static/isSafePath.ts +0 -13
  160. package/src/advanced/static/publicDir.ts +0 -21
  161. package/src/advanced/static/serveStatic.ts +0 -225
  162. package/src/advanced/static/spa.ts +0 -24
  163. package/src/advanced/static/types.ts +0 -159
  164. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  165. package/src/advanced/swagger/buildOperation.ts +0 -61
  166. package/src/advanced/swagger/buildParameters.ts +0 -61
  167. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  168. package/src/advanced/swagger/buildResponses.ts +0 -54
  169. package/src/advanced/swagger/capitalize.ts +0 -5
  170. package/src/advanced/swagger/convertPath.ts +0 -9
  171. package/src/advanced/swagger/createSwagger.ts +0 -12
  172. package/src/advanced/swagger/generateOperationId.ts +0 -21
  173. package/src/advanced/swagger/generateSpec.ts +0 -105
  174. package/src/advanced/swagger/generateSummary.ts +0 -24
  175. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  176. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  177. package/src/advanced/swagger/index.ts +0 -25
  178. package/src/advanced/swagger/swagger.ts +0 -237
  179. package/src/advanced/swagger/types.ts +0 -206
  180. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  181. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  182. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  183. package/src/advanced/testing/factory.ts +0 -509
  184. package/src/advanced/testing/harness.ts +0 -612
  185. package/src/advanced/testing/index.ts +0 -430
  186. package/src/advanced/testing/load-test.ts +0 -618
  187. package/src/advanced/testing/mock-server.ts +0 -498
  188. package/src/advanced/testing/mock.ts +0 -670
  189. package/src/cli/bin.ts +0 -9
  190. package/src/cli/cli.ts +0 -158
  191. package/src/cli/commands/add.ts +0 -178
  192. package/src/cli/commands/build.ts +0 -73
  193. package/src/cli/commands/create.ts +0 -166
  194. package/src/cli/commands/dev.ts +0 -85
  195. package/src/cli/commands/generate.ts +0 -99
  196. package/src/cli/commands/help.ts +0 -95
  197. package/src/cli/commands/init.ts +0 -91
  198. package/src/cli/commands/version.ts +0 -38
  199. package/src/cli/index.ts +0 -6
  200. package/src/cli/templates/generators.ts +0 -359
  201. package/src/cli/templates/index.ts +0 -680
  202. package/src/cli/utils/exec.ts +0 -52
  203. package/src/cli/utils/file-system.ts +0 -78
  204. package/src/cli/utils/logger.ts +0 -111
  205. package/src/core/adapter.ts +0 -88
  206. package/src/core/application.ts +0 -1335
  207. package/src/core/context-pool.ts +0 -127
  208. package/src/core/context.ts +0 -412
  209. package/src/core/index.ts +0 -80
  210. package/src/core/middleware.ts +0 -262
  211. package/src/core/performance/buffer-pool.ts +0 -108
  212. package/src/core/performance/middleware-optimizer.ts +0 -162
  213. package/src/core/plugin/PluginManager.ts +0 -435
  214. package/src/core/plugin/builder.ts +0 -358
  215. package/src/core/plugin/index.ts +0 -50
  216. package/src/core/plugin/types.ts +0 -214
  217. package/src/core/router/file-router.ts +0 -594
  218. package/src/core/router/index.ts +0 -227
  219. package/src/core/router/radix-tree.ts +0 -226
  220. package/src/core/store/index.ts +0 -30
  221. package/src/core/store/registry.ts +0 -178
  222. package/src/core/store/request-store.ts +0 -240
  223. package/src/core/store/types.ts +0 -233
  224. package/src/core/types.ts +0 -574
  225. package/src/database/adapter.ts +0 -35
  226. package/src/database/adapters/index.ts +0 -1
  227. package/src/database/adapters/mysql.ts +0 -669
  228. package/src/database/database.ts +0 -70
  229. package/src/database/dialect.ts +0 -388
  230. package/src/database/index.ts +0 -12
  231. package/src/database/migrations.ts +0 -86
  232. package/src/database/optimizer.ts +0 -125
  233. package/src/database/query-builder.ts +0 -404
  234. package/src/database/realtime.ts +0 -53
  235. package/src/database/schema.ts +0 -71
  236. package/src/database/transactions.ts +0 -56
  237. package/src/database/types.ts +0 -87
  238. package/src/deployment/cluster.ts +0 -471
  239. package/src/deployment/config.ts +0 -454
  240. package/src/deployment/docker.ts +0 -599
  241. package/src/deployment/graceful-shutdown.ts +0 -373
  242. package/src/deployment/index.ts +0 -56
  243. package/src/index.ts +0 -264
  244. package/src/security/adapter.ts +0 -318
  245. package/src/security/auth/JWTPlugin.ts +0 -234
  246. package/src/security/auth/JWTProvider.ts +0 -316
  247. package/src/security/auth/adapter.ts +0 -12
  248. package/src/security/auth/jwt.ts +0 -234
  249. package/src/security/auth/middleware.ts +0 -188
  250. package/src/security/csrf.ts +0 -220
  251. package/src/security/headers.ts +0 -108
  252. package/src/security/index.ts +0 -60
  253. package/src/security/rate-limit/adapter.ts +0 -7
  254. package/src/security/rate-limit/memory.ts +0 -108
  255. package/src/security/rate-limit/middleware.ts +0 -181
  256. package/src/security/sanitization.ts +0 -75
  257. package/src/security/types.ts +0 -240
  258. package/src/security/utils.ts +0 -52
  259. package/tsconfig.json +0 -39
@@ -1,341 +0,0 @@
1
- import { EventEmitter } from 'events';
2
- import { CacheStore, CacheEntry } from './types';
3
-
4
- /**
5
- * Redis cache store configuration
6
- */
7
- export interface RedisCacheConfig {
8
- /**
9
- * Redis connection URL
10
- * @example 'redis://localhost:6379'
11
- */
12
- url?: string;
13
-
14
- /**
15
- * Redis host
16
- * @default 'localhost'
17
- */
18
- host?: string;
19
-
20
- /**
21
- * Redis port
22
- * @default 6379
23
- */
24
- port?: number;
25
-
26
- /**
27
- * Redis password
28
- */
29
- password?: string;
30
-
31
- /**
32
- * Redis database number
33
- * @default 0
34
- */
35
- db?: number;
36
-
37
- /**
38
- * Key prefix for namespacing
39
- * @default 'nexus:'
40
- */
41
- prefix?: string;
42
-
43
- /**
44
- * Connection timeout in milliseconds
45
- * @default 5000
46
- */
47
- connectTimeout?: number;
48
-
49
- /**
50
- * Enable TLS/SSL
51
- * @default false
52
- */
53
- tls?: boolean;
54
-
55
- /**
56
- * Lazy connect - don't connect immediately
57
- * @default false
58
- */
59
- lazyConnect?: boolean;
60
- }
61
-
62
- /**
63
- * Redis client interface
64
- * Supports both 'redis' and 'ioredis' packages
65
- */
66
- export interface RedisClientLike {
67
- get(key: string): Promise<string | null>;
68
- set(key: string, value: string, options?: { EX?: number; PX?: number }): Promise<any>;
69
- setex?(key: string, seconds: number, value: string): Promise<any>;
70
- del(key: string | string[]): Promise<number>;
71
- keys(pattern: string): Promise<string[]>;
72
- flushdb?(): Promise<any>;
73
- flushDb?(): Promise<any>;
74
- quit?(): Promise<any>;
75
- disconnect?(): Promise<any>;
76
- on?(event: string, listener: (...args: any[]) => void): void;
77
- }
78
-
79
- /**
80
- * Redis cache store with full feature support
81
- *
82
- * @example
83
- * ```typescript
84
- * // Using with ioredis
85
- * import Redis from 'ioredis';
86
- *
87
- * const redis = new Redis({ host: 'localhost', port: 6379 });
88
- * const cache = new RedisCacheStore('sessions', { client: redis });
89
- *
90
- * // Using with node-redis
91
- * import { createClient } from 'redis';
92
- *
93
- * const client = createClient({ url: 'redis://localhost:6379' });
94
- * await client.connect();
95
- * const cache = new RedisCacheStore('sessions', { client });
96
- * ```
97
- */
98
- export class RedisCacheStore<Value = unknown> implements CacheStore<Value> {
99
- readonly name: string;
100
- private client: RedisClientLike;
101
- private prefix: string;
102
- private emitter = new EventEmitter();
103
- private connected: boolean = false;
104
-
105
- constructor(
106
- name: string,
107
- options: RedisCacheConfig & { client: RedisClientLike }
108
- ) {
109
- this.name = name;
110
- this.client = options.client;
111
- this.prefix = options.prefix ?? 'nexus:';
112
-
113
- // Listen for connection events if supported
114
- if (this.client.on) {
115
- this.client.on('connect', () => {
116
- this.connected = true;
117
- this.emitter.emit('connect');
118
- });
119
- this.client.on('error', (err) => {
120
- this.emitter.emit('error', err);
121
- });
122
- this.client.on('close', () => {
123
- this.connected = false;
124
- this.emitter.emit('disconnect');
125
- });
126
- } else {
127
- this.connected = true;
128
- }
129
- }
130
-
131
- /**
132
- * Build the full Redis key with prefix
133
- */
134
- private buildKey(key: string): string {
135
- return `${this.prefix}${this.name}:${key}`;
136
- }
137
-
138
- /**
139
- * Get a value from cache
140
- */
141
- async get(key: string): Promise<CacheEntry<Value> | undefined> {
142
- try {
143
- const data = await this.client.get(this.buildKey(key));
144
- if (!data) {
145
- return undefined;
146
- }
147
-
148
- const entry = JSON.parse(data) as CacheEntry<Value>;
149
-
150
- // Check expiration (Redis handles TTL, but we double-check for safety)
151
- if (entry.expiresAt && entry.expiresAt < Date.now()) {
152
- await this.delete(key);
153
- return undefined;
154
- }
155
-
156
- return entry;
157
- } catch (error) {
158
- this.emitter.emit('error', error);
159
- return undefined;
160
- }
161
- }
162
-
163
- /**
164
- * Set a value in cache
165
- */
166
- async set(key: string, entry: CacheEntry<Value>): Promise<void> {
167
- try {
168
- const fullKey = this.buildKey(key);
169
- const data = JSON.stringify(entry);
170
-
171
- if (entry.expiresAt) {
172
- const ttlMs = entry.expiresAt - Date.now();
173
- if (ttlMs > 0) {
174
- const ttlSec = Math.ceil(ttlMs / 1000);
175
- // Try different methods for compatibility
176
- if (this.client.setex) {
177
- // ioredis style
178
- await this.client.setex(fullKey, ttlSec, data);
179
- } else {
180
- // node-redis style
181
- await this.client.set(fullKey, data, { EX: ttlSec });
182
- }
183
- }
184
- } else {
185
- await this.client.set(fullKey, data);
186
- }
187
-
188
- this.emitter.emit('set', key, entry);
189
- } catch (error) {
190
- this.emitter.emit('error', error);
191
- throw error;
192
- }
193
- }
194
-
195
- /**
196
- * Delete a key from cache
197
- */
198
- async delete(key: string): Promise<void> {
199
- try {
200
- await this.client.del(this.buildKey(key));
201
- this.emitter.emit('delete', key);
202
- } catch (error) {
203
- this.emitter.emit('error', error);
204
- throw error;
205
- }
206
- }
207
-
208
- /**
209
- * Clear all keys in this cache namespace
210
- */
211
- async clear(): Promise<void> {
212
- try {
213
- const pattern = `${this.prefix}${this.name}:*`;
214
- const keys = await this.client.keys(pattern);
215
-
216
- if (keys.length > 0) {
217
- await this.client.del(keys);
218
- }
219
-
220
- this.emitter.emit('clear');
221
- } catch (error) {
222
- this.emitter.emit('error', error);
223
- throw error;
224
- }
225
- }
226
-
227
- /**
228
- * Get all keys matching a pattern
229
- */
230
- async keys(pattern?: RegExp): Promise<string[]> {
231
- try {
232
- const redisPattern = `${this.prefix}${this.name}:*`;
233
- const allKeys = await this.client.keys(redisPattern);
234
-
235
- // Remove prefix to get clean keys
236
- const prefixLength = `${this.prefix}${this.name}:`.length;
237
- const cleanKeys = allKeys.map(k => k.slice(prefixLength));
238
-
239
- if (!pattern) {
240
- return cleanKeys;
241
- }
242
-
243
- return cleanKeys.filter(key => pattern.test(key));
244
- } catch (error) {
245
- this.emitter.emit('error', error);
246
- return [];
247
- }
248
- }
249
-
250
- /**
251
- * Check if connected to Redis
252
- */
253
- isConnected(): boolean {
254
- return this.connected;
255
- }
256
-
257
- /**
258
- * Subscribe to cache events
259
- */
260
- on(event: 'set' | 'delete' | 'clear' | 'connect' | 'disconnect' | 'error', listener: (...args: any[]) => void): void {
261
- this.emitter.on(event, listener);
262
- }
263
-
264
- /**
265
- * Disconnect from Redis
266
- */
267
- async disconnect(): Promise<void> {
268
- if (this.client.quit) {
269
- await this.client.quit();
270
- } else if (this.client.disconnect) {
271
- await this.client.disconnect();
272
- }
273
- this.connected = false;
274
- }
275
- }
276
-
277
- /**
278
- * Create a Redis cache store with automatic client creation
279
- * Requires 'ioredis' or 'redis' package to be installed
280
- *
281
- * @example
282
- * ```typescript
283
- * // Auto-create Redis client
284
- * const cache = await createRedisCache('sessions', {
285
- * host: 'localhost',
286
- * port: 6379,
287
- * prefix: 'myapp:'
288
- * });
289
- *
290
- * await cache.set('user:123', { value: { name: 'John' }, expiresAt: Date.now() + 3600000 });
291
- * const user = await cache.get('user:123');
292
- * ```
293
- */
294
- export async function createRedisCache<Value = unknown>(
295
- name: string,
296
- config: RedisCacheConfig = {}
297
- ): Promise<RedisCacheStore<Value>> {
298
- let client: RedisClientLike;
299
-
300
- // Try ioredis first
301
- try {
302
- const Redis = require('ioredis');
303
- client = new Redis({
304
- host: config.host ?? 'localhost',
305
- port: config.port ?? 6379,
306
- password: config.password,
307
- db: config.db ?? 0,
308
- connectTimeout: config.connectTimeout ?? 5000,
309
- tls: config.tls ? {} : undefined,
310
- lazyConnect: config.lazyConnect ?? false
311
- });
312
- } catch {
313
- // Try node-redis
314
- try {
315
- const { createClient } = require('redis');
316
- const url = config.url ?? `redis://${config.host ?? 'localhost'}:${config.port ?? 6379}`;
317
-
318
- client = createClient({
319
- url,
320
- password: config.password,
321
- database: config.db ?? 0,
322
- socket: {
323
- connectTimeout: config.connectTimeout ?? 5000,
324
- tls: config.tls ?? false
325
- }
326
- });
327
-
328
- // node-redis requires explicit connect
329
- await (client as any).connect();
330
- } catch {
331
- throw new Error(
332
- 'Redis client not found. Please install either "ioredis" or "redis" package:\n' +
333
- ' npm install ioredis\n' +
334
- ' # or\n' +
335
- ' npm install redis'
336
- );
337
- }
338
- }
339
-
340
- return new RedisCacheStore<Value>(name, { ...config, client });
341
- }
@@ -1,5 +0,0 @@
1
- export { InMemoryCacheStore } from './InMemoryCacheStore';
2
- export { MultiTierCache } from './MultiTierCache';
3
- export { RedisCacheStore, createRedisCache } from './RedisCacheStore';
4
- export type { RedisCacheConfig, RedisClientLike } from './RedisCacheStore';
5
- export * from './types';
@@ -1,40 +0,0 @@
1
- export interface CacheEntry<Value = unknown> {
2
- value: Value;
3
- expiresAt?: number;
4
- tags?: string[];
5
- meta?: Record<string, any>;
6
- }
7
-
8
- export interface CacheStore<Value = unknown> {
9
- readonly name: string;
10
- get(key: string): Promise<CacheEntry<Value> | undefined>;
11
- set(key: string, entry: CacheEntry<Value>): Promise<void>;
12
- delete(key: string): Promise<void>;
13
- clear(): Promise<void>;
14
- keys?(pattern?: RegExp): Promise<string[]>;
15
- }
16
-
17
- export interface CacheTierConfig<Value = unknown> {
18
- store: CacheStore<Value>;
19
- ttl?: number;
20
- maxSize?: number;
21
- }
22
-
23
- export interface CacheSetOptions<Value = unknown> {
24
- ttl?: number;
25
- tags?: string[];
26
- meta?: CacheEntry<Value>['meta'];
27
- }
28
-
29
- export interface CacheWrapOptions<Value = unknown> extends CacheSetOptions<Value> {
30
- refresh?: boolean;
31
- }
32
-
33
- export interface MemoizeOptions<Value = unknown> extends CacheSetOptions<Value> {
34
- keyResolver?: (...args: any[]) => string;
35
- }
36
-
37
- export interface TagIndexEntry {
38
- keys: Set<string>;
39
- expiresAt?: number;
40
- }
@@ -1,42 +0,0 @@
1
- interface SimpleDataLoaderBatch<Key, Value> {
2
- keys: Key[];
3
- resolvers: Array<{ resolve: (value: Value) => void; reject: (error: Error) => void }>;
4
- }
5
-
6
- export class SimpleDataLoader<Key = any, Value = any> {
7
- private batch: SimpleDataLoaderBatch<Key, Value> | null = null;
8
-
9
- constructor(private batchLoadFn: (keys: Key[]) => Promise<Map<Key, Value>>) { }
10
-
11
- load(key: Key): Promise<Value> {
12
- if (!this.batch) {
13
- this.batch = { keys: [], resolvers: [] };
14
- queueMicrotask(() => this.dispatch());
15
- }
16
-
17
- return new Promise<Value>((resolve, reject) => {
18
- this.batch!.keys.push(key);
19
- this.batch!.resolvers.push({ resolve, reject });
20
- });
21
- }
22
-
23
- private async dispatch() {
24
- if (!this.batch) return;
25
- const current = this.batch;
26
- this.batch = null;
27
-
28
- try {
29
- const result = await this.batchLoadFn(current.keys);
30
- current.keys.forEach((key, index) => {
31
- const value = result.get(key);
32
- if (value === undefined) {
33
- current.resolvers[index].reject(new Error(`DataLoader missing key "${String(key)}"`));
34
- } else {
35
- current.resolvers[index].resolve(value);
36
- }
37
- });
38
- } catch (error) {
39
- current.resolvers.forEach(resolver => resolver.reject(error as Error));
40
- }
41
- }
42
- }
@@ -1,22 +0,0 @@
1
- /**
2
- * Nexus GraphQL Module
3
- *
4
- * Optional GraphQL integration for Nexus framework.
5
- * Requires `graphql` as peer dependency.
6
- *
7
- * Install: npm install graphql
8
- *
9
- * Usage:
10
- * import { GraphQLServer, SimpleDataLoader } from '@engjts/server/graphql';
11
- */
12
-
13
- export { GraphQLServer } from './server';
14
- export { SimpleDataLoader } from './SimpleDataLoader';
15
- export type {
16
- GraphQLComplexityOptions,
17
- GraphQLCacheOptions,
18
- GraphQLRequestPayload,
19
- GraphQLServerOptions,
20
- SimpleDataLoaderBatch,
21
- DataLoaderFactory
22
- } from './types';
@@ -1,252 +0,0 @@
1
- import {
2
- DocumentNode,
3
- ExecutionResult,
4
- GraphQLSchema,
5
- Kind,
6
- OperationDefinitionNode,
7
- execute,
8
- getOperationAST,
9
- parse,
10
- validate
11
- } from 'graphql';
12
- import { Context, Handler, Response } from '../../core/types';
13
- import { GraphQLComplexityOptions, GraphQLRequestPayload, GraphQLServerOptions } from './types';
14
-
15
- export class GraphQLServer {
16
- private schema: GraphQLSchema;
17
- private options: GraphQLServerOptions;
18
-
19
- constructor(options: GraphQLServerOptions) {
20
- this.schema = options.schema;
21
- this.options = {
22
- playground: options.playground ?? false,
23
- introspection: options.introspection ?? process.env.NODE_ENV !== 'production',
24
- dataloaders: options.dataloaders ?? true,
25
- ...options
26
- };
27
- }
28
-
29
- /**
30
- * Returns a framework route handler
31
- */
32
- handler(): Handler {
33
- return async (ctx: Context): Promise<Response> => {
34
- if (ctx.method === 'GET' && this.shouldRenderPlayground(ctx)) {
35
- return ctx.html(this.renderPlayground());
36
- }
37
-
38
- const payload = await this.parseRequest(ctx);
39
- const document = this.parseDocument(payload.query);
40
- this.enforceRules(document, payload);
41
-
42
- const cacheKey = await this.computeCacheKey(payload);
43
- if (cacheKey) {
44
- const cached = await this.options.cache!.instance.get(cacheKey);
45
- if (cached) {
46
- return this.buildResponse(cached as ExecutionResult);
47
- }
48
- }
49
-
50
- const contextValue = await this.buildContext(ctx);
51
- const result = await execute({
52
- schema: this.schema,
53
- document,
54
- variableValues: payload.variables,
55
- operationName: payload.operationName,
56
- contextValue
57
- });
58
-
59
- if (cacheKey && !result.errors) {
60
- await this.options.cache!.instance.set(cacheKey, result, { ttl: this.options.cache?.ttl });
61
- }
62
-
63
- return this.buildResponse(result);
64
- };
65
- }
66
-
67
- private shouldRenderPlayground(ctx: Context): boolean {
68
- if (!this.options.playground) {
69
- return false;
70
- }
71
- const accept = ctx.headers['accept'];
72
- if (!accept) return false;
73
- const acceptHeader = Array.isArray(accept) ? accept.join(',') : accept;
74
- return acceptHeader.includes('text/html');
75
- }
76
-
77
- private async parseRequest(ctx: Context): Promise<GraphQLRequestPayload> {
78
- if (ctx.method === 'GET') {
79
- return {
80
- query: ctx.query.query,
81
- variables: this.safeParseJSON(ctx.query.variables),
82
- operationName: ctx.query.operationName as string | undefined
83
- };
84
- }
85
-
86
- if (typeof ctx.body === 'string') {
87
- return JSON.parse(ctx.body);
88
- }
89
-
90
- return ctx.body ?? {};
91
- }
92
-
93
- private parseDocument(query?: string): DocumentNode {
94
- if (!query) {
95
- throw new Error('GraphQL query is required');
96
- }
97
- return parse(query);
98
- }
99
-
100
- private enforceRules(document: DocumentNode, payload: GraphQLRequestPayload) {
101
- if (!this.options.introspection) {
102
- const operation = getOperationAST(document, payload.operationName || undefined);
103
- if (operation?.operation === 'query' && operation.name?.value === '__schema') {
104
- throw new Error('Introspection is disabled');
105
- }
106
- }
107
-
108
- const validationErrors = validate(this.schema, document);
109
- if (validationErrors.length > 0) {
110
- throw validationErrors[0];
111
- }
112
-
113
- if (this.options.depthLimit !== undefined) {
114
- const depth = this.calculateDepth(document);
115
- if (depth > this.options.depthLimit) {
116
- throw new Error(`Query depth ${depth} exceeds limit of ${this.options.depthLimit}`);
117
- }
118
- }
119
-
120
- if (this.options.complexity) {
121
- const complexity = this.calculateComplexity(document, this.options.complexity);
122
- if (complexity > this.options.complexity.limit) {
123
- throw new Error(`Query complexity ${complexity} exceeds limit of ${this.options.complexity.limit}`);
124
- }
125
- }
126
- }
127
-
128
- private async buildContext(ctx: Context) {
129
- const base = typeof this.options.context === 'function'
130
- ? await this.options.context({ ctx })
131
- : this.options.context ?? {};
132
-
133
- if (this.options.dataloaders) {
134
- Object.assign(base, { loaders: {} });
135
- }
136
-
137
- return { ...base, request: ctx };
138
- }
139
-
140
- private calculateDepth(document: DocumentNode): number {
141
- let maxDepth = 0;
142
-
143
- const traverse = (node: any, depth: number) => {
144
- if (!node.selectionSet) {
145
- return;
146
- }
147
- depth += 1;
148
- maxDepth = Math.max(maxDepth, depth);
149
- for (const selection of node.selectionSet.selections) {
150
- traverse(selection, depth);
151
- }
152
- };
153
-
154
- for (const definition of document.definitions) {
155
- if (definition.kind === Kind.OPERATION_DEFINITION) {
156
- traverse(definition, 0);
157
- }
158
- }
159
-
160
- return maxDepth;
161
- }
162
-
163
- private calculateComplexity(
164
- document: DocumentNode,
165
- options: GraphQLComplexityOptions
166
- ): number {
167
- const costs = options.cost || {};
168
- const defaultCost = options.defaultCost ?? 1;
169
- let complexity = 0;
170
-
171
- const visitNode = (node: OperationDefinitionNode | any) => {
172
- if (!node.selectionSet) {
173
- return;
174
- }
175
-
176
- for (const selection of node.selectionSet.selections) {
177
- if (selection.kind === Kind.FIELD) {
178
- const fieldName = selection.name.value;
179
- complexity += costs[fieldName] ?? defaultCost;
180
- }
181
- visitNode(selection);
182
- }
183
- };
184
-
185
- for (const definition of document.definitions) {
186
- if (definition.kind === Kind.OPERATION_DEFINITION) {
187
- visitNode(definition);
188
- }
189
- }
190
-
191
- return complexity;
192
- }
193
-
194
- private async computeCacheKey(payload: GraphQLRequestPayload): Promise<string | null> {
195
- if (!this.options.cache) {
196
- return null;
197
- }
198
-
199
- const generator = this.options.cache.keyGenerator ??
200
- ((data: GraphQLRequestPayload) => JSON.stringify(data));
201
-
202
- return generator(payload);
203
- }
204
-
205
- private buildResponse(result: ExecutionResult): Response {
206
- if (result.errors && this.options.formatError) {
207
- result = {
208
- ...result,
209
- errors: result.errors.map(err => this.options.formatError!(err))
210
- };
211
- }
212
-
213
- return {
214
- statusCode: result.errors ? 400 : 200,
215
- headers: { 'Content-Type': 'application/json' },
216
- body: JSON.stringify(result)
217
- };
218
- }
219
-
220
- private safeParseJSON(value: any) {
221
- if (!value) return undefined;
222
- try {
223
- if (typeof value === 'string') {
224
- return JSON.parse(value);
225
- }
226
- return value;
227
- } catch {
228
- return undefined;
229
- }
230
- }
231
-
232
- private renderPlayground() {
233
- return `
234
- <!DOCTYPE html>
235
- <html>
236
- <head>
237
- <meta charset=utf-8/>
238
- <title>GraphQL Playground</title>
239
- <link rel="stylesheet" href="https://unpkg.com/graphql-playground-react/build/static/css/index.css" />
240
- <link rel="shortcut icon" href="https://raw.githubusercontent.com/graphql/graphql-playground/main/packages/graphql-playground-react/public/favicon.png" />
241
- <script src="https://unpkg.com/graphql-playground-react/build/static/js/middleware.js"></script>
242
- </head>
243
- <body>
244
- <div id="root" />
245
- <script>window.addEventListener('load', function () {
246
- GraphQLPlayground.init(document.getElementById('root'), { endpoint: '/graphql' })
247
- })</script>
248
- </body>
249
- </html>`;
250
- }
251
- }
252
-