@hazeljs/core 0.2.0-rc.8 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -235,7 +235,7 @@ getProfile(@CurrentUser user: User) {
|
|
|
235
235
|
}
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
-
Use the custom param decorator **without** parentheses: `@CurrentUser`. See the [full API reference](https://hazeljs.
|
|
238
|
+
Use the custom param decorator **without** parentheses: `@CurrentUser`. See the [full API reference](https://hazeljs.ai/docs/api-reference) for `ParamDecoratorContext` and `CUSTOM_METADATA_PREFIX`.
|
|
239
239
|
|
|
240
240
|
## Middleware
|
|
241
241
|
|
|
@@ -550,11 +550,11 @@ Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTI
|
|
|
550
550
|
|
|
551
551
|
## License
|
|
552
552
|
|
|
553
|
-
Apache 2.0 © [HazelJS](https://hazeljs.
|
|
553
|
+
Apache 2.0 © [HazelJS](https://hazeljs.ai)
|
|
554
554
|
|
|
555
555
|
## Links
|
|
556
556
|
|
|
557
|
-
- [Documentation](https://hazeljs.
|
|
557
|
+
- [Documentation](https://hazeljs.ai/docs)
|
|
558
558
|
- [GitHub](https://github.com/hazel-js/hazeljs)
|
|
559
559
|
- [Issues](https://github.com/hazel-js/hazeljs/issues)
|
|
560
560
|
- [Discord](https://discord.com/channels/1448263814238965833/1448263814859456575)
|
|
@@ -163,4 +163,50 @@ describe('Interceptors', () => {
|
|
|
163
163
|
expect(result2).toEqual(complexData);
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
|
+
describe('RetryInterceptor', () => {
|
|
167
|
+
it('should call next once when no retry options', async () => {
|
|
168
|
+
const interceptor = new interceptor_1.RetryInterceptor();
|
|
169
|
+
const next = jest.fn().mockResolvedValue({ ok: true });
|
|
170
|
+
const result = await interceptor.intercept(context, next);
|
|
171
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
172
|
+
expect(result).toEqual({ ok: true });
|
|
173
|
+
});
|
|
174
|
+
it('should call next once when retry count is 0', async () => {
|
|
175
|
+
const interceptor = new interceptor_1.RetryInterceptor();
|
|
176
|
+
context.retryOptions = {
|
|
177
|
+
count: 0,
|
|
178
|
+
};
|
|
179
|
+
const next = jest.fn().mockResolvedValue({ ok: true });
|
|
180
|
+
await interceptor.intercept(context, next);
|
|
181
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
182
|
+
});
|
|
183
|
+
it('should retry on failure and succeed on second attempt', async () => {
|
|
184
|
+
const interceptor = new interceptor_1.RetryInterceptor();
|
|
185
|
+
context.retryOptions =
|
|
186
|
+
{ count: 2, delay: 1 };
|
|
187
|
+
const next = jest
|
|
188
|
+
.fn()
|
|
189
|
+
.mockRejectedValueOnce(new Error('fail'))
|
|
190
|
+
.mockResolvedValueOnce({ ok: true });
|
|
191
|
+
const result = await interceptor.intercept(context, next);
|
|
192
|
+
expect(next).toHaveBeenCalledTimes(2);
|
|
193
|
+
expect(result).toEqual({ ok: true });
|
|
194
|
+
});
|
|
195
|
+
it('should throw after exhausting retries', async () => {
|
|
196
|
+
const interceptor = new interceptor_1.RetryInterceptor();
|
|
197
|
+
context.retryOptions =
|
|
198
|
+
{ count: 2, delay: 1 };
|
|
199
|
+
const next = jest.fn().mockRejectedValue(new Error('always fail'));
|
|
200
|
+
await expect(interceptor.intercept(context, next)).rejects.toThrow('always fail');
|
|
201
|
+
expect(next).toHaveBeenCalledTimes(3);
|
|
202
|
+
});
|
|
203
|
+
it('should not retry when retryIf returns false', async () => {
|
|
204
|
+
const interceptor = new interceptor_1.RetryInterceptor();
|
|
205
|
+
context.retryOptions =
|
|
206
|
+
{ count: 2, retryIf: () => false };
|
|
207
|
+
const next = jest.fn().mockRejectedValue(new Error('no retry'));
|
|
208
|
+
await expect(interceptor.intercept(context, next)).rejects.toThrow('no retry');
|
|
209
|
+
expect(next).toHaveBeenCalledTimes(1);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
166
212
|
});
|
|
@@ -22,6 +22,7 @@ describe('GlobalMiddlewareManager', () => {
|
|
|
22
22
|
mockRes = {
|
|
23
23
|
setHeader: jest.fn(),
|
|
24
24
|
status: jest.fn().mockReturnThis(),
|
|
25
|
+
end: jest.fn().mockReturnThis(),
|
|
25
26
|
json: jest.fn(),
|
|
26
27
|
};
|
|
27
28
|
});
|
|
@@ -256,4 +257,36 @@ describe('GlobalMiddlewareManager', () => {
|
|
|
256
257
|
expect(manager.getMiddleware()).toHaveLength(1);
|
|
257
258
|
});
|
|
258
259
|
});
|
|
260
|
+
describe('CorsMiddleware (global-middleware)', () => {
|
|
261
|
+
it('should set CORS headers and call next', () => {
|
|
262
|
+
const middleware = new global_middleware_1.CorsMiddleware();
|
|
263
|
+
const next = jest.fn();
|
|
264
|
+
middleware.use(mockReq, mockRes, next);
|
|
265
|
+
expect(mockRes.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Origin', '*');
|
|
266
|
+
expect(mockRes.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
|
|
267
|
+
expect(next).toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
it('should set credentials when options.credentials is true', () => {
|
|
270
|
+
const middleware = new global_middleware_1.CorsMiddleware({ credentials: true });
|
|
271
|
+
const next = jest.fn();
|
|
272
|
+
middleware.use(mockReq, mockRes, next);
|
|
273
|
+
expect(mockRes.setHeader).toHaveBeenCalledWith('Access-Control-Allow-Credentials', 'true');
|
|
274
|
+
});
|
|
275
|
+
it('should respond 204 for OPTIONS and not call next', () => {
|
|
276
|
+
const middleware = new global_middleware_1.CorsMiddleware();
|
|
277
|
+
const next = jest.fn();
|
|
278
|
+
mockReq.method = 'OPTIONS';
|
|
279
|
+
middleware.use(mockReq, mockRes, next);
|
|
280
|
+
expect(mockRes.status).toHaveBeenCalledWith(204);
|
|
281
|
+
expect(next).not.toHaveBeenCalled();
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
describe('LoggerMiddleware (global-middleware)', () => {
|
|
285
|
+
it('should log request and response and call next', async () => {
|
|
286
|
+
const middleware = new global_middleware_1.LoggerMiddleware();
|
|
287
|
+
const next = jest.fn().mockResolvedValue(undefined);
|
|
288
|
+
await middleware.use(mockReq, mockRes, next);
|
|
289
|
+
expect(next).toHaveBeenCalled();
|
|
290
|
+
});
|
|
291
|
+
});
|
|
259
292
|
});
|
|
@@ -191,6 +191,16 @@ describe('RateLimitMiddleware', () => {
|
|
|
191
191
|
expect(true).toBe(true);
|
|
192
192
|
});
|
|
193
193
|
});
|
|
194
|
+
describe('MemoryStore coverage', () => {
|
|
195
|
+
it('should run cleanup interval when using default store', async () => {
|
|
196
|
+
const middleware = new rate_limit_middleware_1.RateLimitMiddleware({ max: 5, windowMs: 60 });
|
|
197
|
+
await middleware.use(mockReq, mockRes, nextFn);
|
|
198
|
+
jest.advanceTimersByTime(61000);
|
|
199
|
+
await middleware.use(mockReq, mockRes, nextFn);
|
|
200
|
+
expect(nextFn).toHaveBeenCalled();
|
|
201
|
+
middleware.destroy();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
194
204
|
describe('edge cases', () => {
|
|
195
205
|
it('should handle missing socket', async () => {
|
|
196
206
|
const middleware = new rate_limit_middleware_1.RateLimitMiddleware({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hazeljs/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Core HazelJS framework - Dependency injection, routing, decorators, and base functionality",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -62,6 +62,6 @@
|
|
|
62
62
|
"bugs": {
|
|
63
63
|
"url": "https://github.com/hazeljs/hazel-js/issues"
|
|
64
64
|
},
|
|
65
|
-
"homepage": "https://hazeljs.
|
|
66
|
-
"gitHead": "
|
|
65
|
+
"homepage": "https://hazeljs.ai",
|
|
66
|
+
"gitHead": "736a56e3d70b51050608cffae2394de3a3487f20"
|
|
67
67
|
}
|