@engjts/nexus 0.1.8 → 0.1.10

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 (221) hide show
  1. package/dist/advanced/playground/playground.js.map +1 -1
  2. package/dist/advanced/static/generateDirectoryListing.d.ts +1 -1
  3. package/dist/advanced/static/generateDirectoryListing.d.ts.map +1 -1
  4. package/dist/advanced/static/generateDirectoryListing.js +12 -6
  5. package/dist/advanced/static/generateDirectoryListing.js.map +1 -1
  6. package/dist/advanced/static/index.d.ts +2 -0
  7. package/dist/advanced/static/index.d.ts.map +1 -1
  8. package/dist/advanced/static/index.js +4 -1
  9. package/dist/advanced/static/index.js.map +1 -1
  10. package/dist/advanced/static/serveStatic.d.ts.map +1 -1
  11. package/dist/advanced/static/serveStatic.js +7 -1
  12. package/dist/advanced/static/serveStatic.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +3 -1
  16. package/dist/index.js.map +1 -1
  17. package/package.json +1 -1
  18. package/BENCHMARK_REPORT.md +0 -343
  19. package/documentation/01-getting-started.md +0 -240
  20. package/documentation/02-context.md +0 -335
  21. package/documentation/03-routing.md +0 -397
  22. package/documentation/04-middleware.md +0 -483
  23. package/documentation/05-validation.md +0 -514
  24. package/documentation/06-error-handling.md +0 -465
  25. package/documentation/07-performance.md +0 -364
  26. package/documentation/08-adapters.md +0 -470
  27. package/documentation/09-api-reference.md +0 -548
  28. package/documentation/10-examples.md +0 -582
  29. package/documentation/11-deployment.md +0 -477
  30. package/documentation/12-sentry.md +0 -620
  31. package/documentation/13-sentry-data-storage.md +0 -996
  32. package/documentation/14-sentry-data-reference.md +0 -457
  33. package/documentation/15-sentry-summary.md +0 -409
  34. package/documentation/16-alerts-system.md +0 -745
  35. package/documentation/17-alert-adapters.md +0 -696
  36. package/documentation/18-alerts-implementation-summary.md +0 -385
  37. package/documentation/19-class-based-routing.md +0 -840
  38. package/documentation/20-websocket-realtime.md +0 -813
  39. package/documentation/21-cache-system.md +0 -510
  40. package/documentation/22-job-queue.md +0 -772
  41. package/documentation/23-sentry-plugin.md +0 -551
  42. package/documentation/24-testing-utilities.md +0 -1287
  43. package/documentation/25-api-versioning.md +0 -533
  44. package/documentation/26-context-store.md +0 -607
  45. package/documentation/27-dependency-injection.md +0 -329
  46. package/documentation/28-lifecycle-hooks.md +0 -521
  47. package/documentation/29-package-structure.md +0 -196
  48. package/documentation/30-plugin-system.md +0 -414
  49. package/documentation/31-jwt-authentication.md +0 -597
  50. package/documentation/32-cli.md +0 -268
  51. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  52. package/documentation/ALERTS-INDEX.md +0 -330
  53. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  54. package/documentation/README.md +0 -178
  55. package/documentation/index.html +0 -34
  56. package/modern_framework_paper.md +0 -1870
  57. package/public/css/style.css +0 -87
  58. package/public/index.html +0 -34
  59. package/public/js/app.js +0 -27
  60. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  61. package/src/advanced/cache/MultiTierCache.ts +0 -194
  62. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  63. package/src/advanced/cache/index.ts +0 -5
  64. package/src/advanced/cache/types.ts +0 -40
  65. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  66. package/src/advanced/graphql/index.ts +0 -22
  67. package/src/advanced/graphql/server.ts +0 -252
  68. package/src/advanced/graphql/types.ts +0 -42
  69. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  70. package/src/advanced/jobs/JobQueue.ts +0 -556
  71. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  72. package/src/advanced/jobs/index.ts +0 -5
  73. package/src/advanced/jobs/types.ts +0 -70
  74. package/src/advanced/observability/APMManager.ts +0 -163
  75. package/src/advanced/observability/AlertManager.ts +0 -109
  76. package/src/advanced/observability/MetricRegistry.ts +0 -151
  77. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  78. package/src/advanced/observability/StructuredLogger.ts +0 -154
  79. package/src/advanced/observability/TracingManager.ts +0 -117
  80. package/src/advanced/observability/adapters.ts +0 -304
  81. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  82. package/src/advanced/observability/index.ts +0 -11
  83. package/src/advanced/observability/types.ts +0 -174
  84. package/src/advanced/playground/extractPathParams.ts +0 -6
  85. package/src/advanced/playground/generateFieldExample.ts +0 -31
  86. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  87. package/src/advanced/playground/generateSummary.ts +0 -19
  88. package/src/advanced/playground/getTagFromPath.ts +0 -9
  89. package/src/advanced/playground/index.ts +0 -8
  90. package/src/advanced/playground/playground.ts +0 -250
  91. package/src/advanced/playground/types.ts +0 -49
  92. package/src/advanced/playground/zodToExample.ts +0 -16
  93. package/src/advanced/playground/zodToParams.ts +0 -15
  94. package/src/advanced/postman/buildAuth.ts +0 -31
  95. package/src/advanced/postman/buildBody.ts +0 -15
  96. package/src/advanced/postman/buildQueryParams.ts +0 -27
  97. package/src/advanced/postman/buildRequestItem.ts +0 -36
  98. package/src/advanced/postman/buildResponses.ts +0 -11
  99. package/src/advanced/postman/buildUrl.ts +0 -33
  100. package/src/advanced/postman/capitalize.ts +0 -4
  101. package/src/advanced/postman/generateCollection.ts +0 -59
  102. package/src/advanced/postman/generateEnvironment.ts +0 -34
  103. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  104. package/src/advanced/postman/generateFieldExample.ts +0 -45
  105. package/src/advanced/postman/generateName.ts +0 -20
  106. package/src/advanced/postman/generateUUID.ts +0 -11
  107. package/src/advanced/postman/getTagFromPath.ts +0 -10
  108. package/src/advanced/postman/index.ts +0 -28
  109. package/src/advanced/postman/postman.ts +0 -156
  110. package/src/advanced/postman/slugify.ts +0 -7
  111. package/src/advanced/postman/types.ts +0 -140
  112. package/src/advanced/realtime/index.ts +0 -18
  113. package/src/advanced/realtime/websocket.ts +0 -231
  114. package/src/advanced/sentry/index.ts +0 -1236
  115. package/src/advanced/sentry/types.ts +0 -355
  116. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  117. package/src/advanced/static/generateETag.ts +0 -7
  118. package/src/advanced/static/getMimeType.ts +0 -9
  119. package/src/advanced/static/index.ts +0 -32
  120. package/src/advanced/static/isSafePath.ts +0 -13
  121. package/src/advanced/static/publicDir.ts +0 -21
  122. package/src/advanced/static/serveStatic.ts +0 -225
  123. package/src/advanced/static/spa.ts +0 -24
  124. package/src/advanced/static/types.ts +0 -159
  125. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  126. package/src/advanced/swagger/buildOperation.ts +0 -61
  127. package/src/advanced/swagger/buildParameters.ts +0 -61
  128. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  129. package/src/advanced/swagger/buildResponses.ts +0 -54
  130. package/src/advanced/swagger/capitalize.ts +0 -5
  131. package/src/advanced/swagger/convertPath.ts +0 -9
  132. package/src/advanced/swagger/createSwagger.ts +0 -12
  133. package/src/advanced/swagger/generateOperationId.ts +0 -21
  134. package/src/advanced/swagger/generateSpec.ts +0 -105
  135. package/src/advanced/swagger/generateSummary.ts +0 -24
  136. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  137. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  138. package/src/advanced/swagger/index.ts +0 -25
  139. package/src/advanced/swagger/swagger.ts +0 -237
  140. package/src/advanced/swagger/types.ts +0 -206
  141. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  142. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  143. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  144. package/src/advanced/testing/factory.ts +0 -509
  145. package/src/advanced/testing/harness.ts +0 -612
  146. package/src/advanced/testing/index.ts +0 -430
  147. package/src/advanced/testing/load-test.ts +0 -618
  148. package/src/advanced/testing/mock-server.ts +0 -498
  149. package/src/advanced/testing/mock.ts +0 -670
  150. package/src/cli/bin.ts +0 -9
  151. package/src/cli/cli.ts +0 -158
  152. package/src/cli/commands/add.ts +0 -178
  153. package/src/cli/commands/build.ts +0 -73
  154. package/src/cli/commands/create.ts +0 -166
  155. package/src/cli/commands/dev.ts +0 -85
  156. package/src/cli/commands/generate.ts +0 -99
  157. package/src/cli/commands/help.ts +0 -95
  158. package/src/cli/commands/init.ts +0 -91
  159. package/src/cli/commands/version.ts +0 -38
  160. package/src/cli/index.ts +0 -6
  161. package/src/cli/templates/generators.ts +0 -359
  162. package/src/cli/templates/index.ts +0 -680
  163. package/src/cli/utils/exec.ts +0 -52
  164. package/src/cli/utils/file-system.ts +0 -78
  165. package/src/cli/utils/logger.ts +0 -111
  166. package/src/core/adapter.ts +0 -88
  167. package/src/core/application.ts +0 -1453
  168. package/src/core/context-pool.ts +0 -79
  169. package/src/core/context.ts +0 -856
  170. package/src/core/index.ts +0 -94
  171. package/src/core/middleware.ts +0 -272
  172. package/src/core/performance/buffer-pool.ts +0 -108
  173. package/src/core/performance/middleware-optimizer.ts +0 -162
  174. package/src/core/plugin/PluginManager.ts +0 -435
  175. package/src/core/plugin/builder.ts +0 -358
  176. package/src/core/plugin/index.ts +0 -50
  177. package/src/core/plugin/types.ts +0 -214
  178. package/src/core/router/file-router.ts +0 -623
  179. package/src/core/router/index.ts +0 -260
  180. package/src/core/router/radix-tree.ts +0 -242
  181. package/src/core/serializer.ts +0 -397
  182. package/src/core/store/index.ts +0 -30
  183. package/src/core/store/registry.ts +0 -178
  184. package/src/core/store/request-store.ts +0 -240
  185. package/src/core/store/types.ts +0 -233
  186. package/src/core/types.ts +0 -616
  187. package/src/database/adapter.ts +0 -35
  188. package/src/database/adapters/index.ts +0 -1
  189. package/src/database/adapters/mysql.ts +0 -669
  190. package/src/database/database.ts +0 -70
  191. package/src/database/dialect.ts +0 -388
  192. package/src/database/index.ts +0 -12
  193. package/src/database/migrations.ts +0 -86
  194. package/src/database/optimizer.ts +0 -125
  195. package/src/database/query-builder.ts +0 -404
  196. package/src/database/realtime.ts +0 -53
  197. package/src/database/schema.ts +0 -71
  198. package/src/database/transactions.ts +0 -56
  199. package/src/database/types.ts +0 -87
  200. package/src/deployment/cluster.ts +0 -471
  201. package/src/deployment/config.ts +0 -454
  202. package/src/deployment/docker.ts +0 -599
  203. package/src/deployment/graceful-shutdown.ts +0 -373
  204. package/src/deployment/index.ts +0 -56
  205. package/src/index.ts +0 -281
  206. package/src/security/adapter.ts +0 -318
  207. package/src/security/auth/JWTPlugin.ts +0 -234
  208. package/src/security/auth/JWTProvider.ts +0 -316
  209. package/src/security/auth/adapter.ts +0 -12
  210. package/src/security/auth/jwt.ts +0 -234
  211. package/src/security/auth/middleware.ts +0 -188
  212. package/src/security/csrf.ts +0 -220
  213. package/src/security/headers.ts +0 -108
  214. package/src/security/index.ts +0 -60
  215. package/src/security/rate-limit/adapter.ts +0 -7
  216. package/src/security/rate-limit/memory.ts +0 -108
  217. package/src/security/rate-limit/middleware.ts +0 -181
  218. package/src/security/sanitization.ts +0 -75
  219. package/src/security/types.ts +0 -240
  220. package/src/security/utils.ts +0 -52
  221. 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
-