@geekmidas/services 0.1.0 → 0.2.0

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 (46) hide show
  1. package/dist/ServiceDiscovery-BO2FzEGt.cjs +200 -0
  2. package/dist/ServiceDiscovery-BO2FzEGt.cjs.map +1 -0
  3. package/dist/ServiceDiscovery-CfVBrQHt.d.cts +189 -0
  4. package/dist/ServiceDiscovery-CfVBrQHt.d.cts.map +1 -0
  5. package/dist/ServiceDiscovery-DLMyH_yM.mjs +195 -0
  6. package/dist/ServiceDiscovery-DLMyH_yM.mjs.map +1 -0
  7. package/dist/ServiceDiscovery-DpeEKNe2.d.mts +189 -0
  8. package/dist/ServiceDiscovery-DpeEKNe2.d.mts.map +1 -0
  9. package/dist/ServiceDiscovery.cjs +4 -0
  10. package/dist/ServiceDiscovery.d.cts +3 -0
  11. package/dist/ServiceDiscovery.d.mts +3 -0
  12. package/dist/ServiceDiscovery.mjs +4 -0
  13. package/dist/context-B5YTspJR.mjs +61 -0
  14. package/dist/context-B5YTspJR.mjs.map +1 -0
  15. package/dist/context-BVeZOvOd.d.cts +45 -0
  16. package/dist/context-BVeZOvOd.d.cts.map +1 -0
  17. package/dist/context-DMExgNzl.d.mts +45 -0
  18. package/dist/context-DMExgNzl.d.mts.map +1 -0
  19. package/dist/context-DUTDtYd2.cjs +95 -0
  20. package/dist/context-DUTDtYd2.cjs.map +1 -0
  21. package/dist/context.cjs +4 -0
  22. package/dist/context.d.cts +3 -0
  23. package/dist/context.d.mts +3 -0
  24. package/dist/context.mjs +3 -0
  25. package/dist/index.cjs +5 -193
  26. package/dist/index.d.cts +4 -227
  27. package/dist/index.d.mts +4 -227
  28. package/dist/index.mjs +3 -192
  29. package/dist/types-CcHmCx_U.d.mts +86 -0
  30. package/dist/types-CcHmCx_U.d.mts.map +1 -0
  31. package/dist/types-D7d_yeU5.d.cts +86 -0
  32. package/dist/types-D7d_yeU5.d.cts.map +1 -0
  33. package/dist/types.cjs +0 -0
  34. package/dist/types.d.cts +2 -0
  35. package/dist/types.d.mts +2 -0
  36. package/dist/types.mjs +0 -0
  37. package/package.json +10 -5
  38. package/src/ServiceDiscovery.ts +254 -0
  39. package/src/__tests__/context.spec.ts +276 -0
  40. package/src/__tests__/index.spec.ts +556 -558
  41. package/src/context.ts +91 -0
  42. package/src/index.ts +15 -295
  43. package/src/types.ts +85 -0
  44. package/tsconfig.json +9 -0
  45. package/dist/index.cjs.map +0 -1
  46. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,276 @@
1
+ import { ConsoleLogger } from '@geekmidas/logger/console';
2
+ import { describe, expect, it } from 'vitest';
3
+ import { runWithRequestContext, serviceContext } from '../context';
4
+
5
+ describe('Request Context', () => {
6
+ const logger = new ConsoleLogger({ app: 'test' });
7
+
8
+ describe('serviceContext', () => {
9
+ describe('hasContext', () => {
10
+ it('should return false outside request context', () => {
11
+ expect(serviceContext.hasContext()).toBe(false);
12
+ });
13
+
14
+ it('should return true inside request context', async () => {
15
+ await runWithRequestContext(
16
+ { logger, requestId: 'test-id', startTime: Date.now() },
17
+ async () => {
18
+ expect(serviceContext.hasContext()).toBe(true);
19
+ },
20
+ );
21
+ });
22
+ });
23
+
24
+ describe('getLogger', () => {
25
+ it('should throw outside request context', () => {
26
+ expect(() => serviceContext.getLogger()).toThrow(
27
+ 'ServiceContext.getLogger() called outside request context',
28
+ );
29
+ });
30
+
31
+ it('should return logger inside request context', async () => {
32
+ await runWithRequestContext(
33
+ { logger, requestId: 'test-id', startTime: Date.now() },
34
+ async () => {
35
+ expect(serviceContext.getLogger()).toBe(logger);
36
+ },
37
+ );
38
+ });
39
+ });
40
+
41
+ describe('getRequestId', () => {
42
+ it('should throw outside request context', () => {
43
+ expect(() => serviceContext.getRequestId()).toThrow(
44
+ 'ServiceContext.getRequestId() called outside request context',
45
+ );
46
+ });
47
+
48
+ it('should return requestId inside request context', async () => {
49
+ await runWithRequestContext(
50
+ { logger, requestId: 'my-request-id-123', startTime: Date.now() },
51
+ async () => {
52
+ expect(serviceContext.getRequestId()).toBe('my-request-id-123');
53
+ },
54
+ );
55
+ });
56
+ });
57
+
58
+ describe('getRequestStartTime', () => {
59
+ it('should throw outside request context', () => {
60
+ expect(() => serviceContext.getRequestStartTime()).toThrow(
61
+ 'ServiceContext.getRequestStartTime() called outside request context',
62
+ );
63
+ });
64
+
65
+ it('should return startTime inside request context', async () => {
66
+ const startTime = Date.now();
67
+ await runWithRequestContext(
68
+ { logger, requestId: 'test-id', startTime },
69
+ async () => {
70
+ expect(serviceContext.getRequestStartTime()).toBe(startTime);
71
+ },
72
+ );
73
+ });
74
+ });
75
+ });
76
+
77
+ describe('runWithRequestContext', () => {
78
+ it('should return synchronous value', () => {
79
+ const result = runWithRequestContext(
80
+ { logger, requestId: 'test-id', startTime: Date.now() },
81
+ () => 'sync-result',
82
+ );
83
+
84
+ expect(result).toBe('sync-result');
85
+ });
86
+
87
+ it('should return async value', async () => {
88
+ const result = await runWithRequestContext(
89
+ { logger, requestId: 'test-id', startTime: Date.now() },
90
+ async () => {
91
+ await new Promise((resolve) => setTimeout(resolve, 10));
92
+ return 'async-result';
93
+ },
94
+ );
95
+
96
+ expect(result).toBe('async-result');
97
+ });
98
+
99
+ it('should provide isolated context per run', async () => {
100
+ const results: string[] = [];
101
+
102
+ await Promise.all([
103
+ runWithRequestContext(
104
+ { logger, requestId: 'request-1', startTime: Date.now() },
105
+ async () => {
106
+ await new Promise((resolve) => setTimeout(resolve, 10));
107
+ results.push(`1:${serviceContext.getRequestId()}`);
108
+ },
109
+ ),
110
+ runWithRequestContext(
111
+ { logger, requestId: 'request-2', startTime: Date.now() },
112
+ async () => {
113
+ await new Promise((resolve) => setTimeout(resolve, 5));
114
+ results.push(`2:${serviceContext.getRequestId()}`);
115
+ },
116
+ ),
117
+ runWithRequestContext(
118
+ { logger, requestId: 'request-3', startTime: Date.now() },
119
+ async () => {
120
+ results.push(`3:${serviceContext.getRequestId()}`);
121
+ },
122
+ ),
123
+ ]);
124
+
125
+ // Each request should have its own isolated context
126
+ expect(results).toContain('1:request-1');
127
+ expect(results).toContain('2:request-2');
128
+ expect(results).toContain('3:request-3');
129
+ });
130
+
131
+ it('should propagate context through async operations', async () => {
132
+ const capturedIds: string[] = [];
133
+
134
+ async function nestedOperation() {
135
+ // Should still have access to context from parent
136
+ capturedIds.push(serviceContext.getRequestId());
137
+ await new Promise((resolve) => setTimeout(resolve, 1));
138
+ capturedIds.push(serviceContext.getRequestId());
139
+ }
140
+
141
+ await runWithRequestContext(
142
+ { logger, requestId: 'parent-context', startTime: Date.now() },
143
+ async () => {
144
+ capturedIds.push(serviceContext.getRequestId());
145
+ await nestedOperation();
146
+ capturedIds.push(serviceContext.getRequestId());
147
+ },
148
+ );
149
+
150
+ // All operations should see the same context
151
+ expect(capturedIds).toEqual([
152
+ 'parent-context',
153
+ 'parent-context',
154
+ 'parent-context',
155
+ 'parent-context',
156
+ ]);
157
+ });
158
+
159
+ it('should handle errors while preserving context', async () => {
160
+ let capturedIdBeforeError: string | undefined;
161
+ let capturedIdInCatch: string | undefined;
162
+
163
+ await runWithRequestContext(
164
+ { logger, requestId: 'error-context', startTime: Date.now() },
165
+ async () => {
166
+ capturedIdBeforeError = serviceContext.getRequestId();
167
+ try {
168
+ throw new Error('Test error');
169
+ } catch {
170
+ capturedIdInCatch = serviceContext.getRequestId();
171
+ }
172
+ },
173
+ );
174
+
175
+ expect(capturedIdBeforeError).toBe('error-context');
176
+ expect(capturedIdInCatch).toBe('error-context');
177
+ });
178
+
179
+ it('should propagate exceptions', async () => {
180
+ await expect(
181
+ runWithRequestContext(
182
+ { logger, requestId: 'test-id', startTime: Date.now() },
183
+ async () => {
184
+ throw new Error('Test exception');
185
+ },
186
+ ),
187
+ ).rejects.toThrow('Test exception');
188
+ });
189
+
190
+ it('should allow nested runWithRequestContext calls', async () => {
191
+ const capturedIds: string[] = [];
192
+
193
+ await runWithRequestContext(
194
+ { logger, requestId: 'outer', startTime: Date.now() },
195
+ async () => {
196
+ capturedIds.push(serviceContext.getRequestId());
197
+
198
+ // Nested context should override
199
+ await runWithRequestContext(
200
+ { logger, requestId: 'inner', startTime: Date.now() },
201
+ async () => {
202
+ capturedIds.push(serviceContext.getRequestId());
203
+ },
204
+ );
205
+
206
+ // Should return to outer context
207
+ capturedIds.push(serviceContext.getRequestId());
208
+ },
209
+ );
210
+
211
+ expect(capturedIds).toEqual(['outer', 'inner', 'outer']);
212
+ });
213
+ });
214
+
215
+ describe('Integration with services', () => {
216
+ it('should allow services to access context', async () => {
217
+ // Simulating a service that accesses context
218
+ const myService = {
219
+ getRequestInfo() {
220
+ if (!serviceContext.hasContext()) {
221
+ return null;
222
+ }
223
+ return {
224
+ requestId: serviceContext.getRequestId(),
225
+ startTime: serviceContext.getRequestStartTime(),
226
+ };
227
+ },
228
+ };
229
+
230
+ // Outside context
231
+ expect(myService.getRequestInfo()).toBeNull();
232
+
233
+ // Inside context
234
+ const startTime = Date.now();
235
+ await runWithRequestContext(
236
+ { logger, requestId: 'service-request', startTime },
237
+ async () => {
238
+ const info = myService.getRequestInfo();
239
+ expect(info).not.toBeNull();
240
+ expect(info?.requestId).toBe('service-request');
241
+ expect(info?.startTime).toBe(startTime);
242
+ },
243
+ );
244
+ });
245
+
246
+ it('should allow logger access for request-scoped logging', async () => {
247
+ const logMessages: string[] = [];
248
+ const requestLogger = new ConsoleLogger({
249
+ app: 'test',
250
+ });
251
+
252
+ // Override info method to capture logs
253
+ const originalInfo = requestLogger.info.bind(requestLogger);
254
+ requestLogger.info = (...args: Parameters<typeof requestLogger.info>) => {
255
+ logMessages.push(
256
+ typeof args[0] === 'string' ? args[0] : JSON.stringify(args[0]),
257
+ );
258
+ return originalInfo(...args);
259
+ };
260
+
261
+ await runWithRequestContext(
262
+ {
263
+ logger: requestLogger,
264
+ requestId: 'log-test',
265
+ startTime: Date.now(),
266
+ },
267
+ async () => {
268
+ const contextLogger = serviceContext.getLogger();
269
+ contextLogger.info('Request started');
270
+ },
271
+ );
272
+
273
+ expect(logMessages).toContain('Request started');
274
+ });
275
+ });
276
+ });