Skip to content

Commit c35ed52

Browse files
chore(deps): upgraded to latest dependencies
upgraded to latest dependencies GH-0
1 parent 7d9a00a commit c35ed52

File tree

12 files changed

+2041
-2680
lines changed

12 files changed

+2041
-2680
lines changed

.github/workflows/trivy.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- uses: actions/checkout@v3
2323

2424
- name: Run Trivy vulnerability scanner in repo mode
25-
uses: aquasecurity/trivy-action@0.28.0
25+
uses: aquasecurity/trivy-action@0.35.0
2626
with:
2727
scan-type: "fs"
2828
scan-ref: "${{ github.workspace }}"

package-lock.json

Lines changed: 1877 additions & 2585 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 39 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -44,52 +44,53 @@
4444
"!*/__tests__"
4545
],
4646
"dependencies": {
47-
"@loopback/boot": "^8.0.4",
48-
"@loopback/context": "^8.0.3",
49-
"@loopback/core": "^7.0.3",
50-
"@loopback/repository": "^8.0.3",
51-
"@loopback/rest": "^15.0.4",
52-
"express-rate-limit": "^6.4.0",
53-
"rate-limit-memcached": "^0.6.0",
47+
"@loopback/boot": "^8.0.11",
48+
"@loopback/context": "^8.0.10",
49+
"@loopback/core": "^7.0.10",
50+
"@loopback/repository": "^8.0.10",
51+
"@loopback/rest": "^15.0.11",
52+
"express-rate-limit": "^8.3.1",
53+
"rate-limit-memcached": "^1.0.1",
5454
"rate-limit-mongo": "^2.3.2",
55-
"rate-limit-redis": "^3.0.1"
55+
"rate-limit-redis": "^4.3.1"
5656
},
5757
"devDependencies": {
58-
"@commitlint/cli": "^17.7.1",
59-
"@commitlint/config-conventional": "^17.7.0",
60-
"@loopback/build": "^12.0.3",
58+
"@commitlint/cli": "^20.5.0",
59+
"@commitlint/config-conventional": "^20.5.0",
60+
"@loopback/build": "^12.0.10",
6161
"@loopback/eslint-config": "^16.0.1",
62-
"@loopback/testlab": "^8.0.3",
63-
"@semantic-release/changelog": "^6.0.1",
64-
"@semantic-release/commit-analyzer": "^9.0.2",
62+
"@loopback/testlab": "^8.0.10",
63+
"@semantic-release/changelog": "^6.0.3",
64+
"@semantic-release/commit-analyzer": "^13.0.1",
6565
"@semantic-release/git": "^10.0.1",
66-
"@semantic-release/github": "^12.0.0",
67-
"@semantic-release/npm": "^13.1.1",
68-
"@semantic-release/release-notes-generator": "^10.0.3",
69-
"@types/express-rate-limit": "^5.0.0",
70-
"@types/memcached": "^2.2.6",
71-
"@types/node": "^18.11.9",
72-
"@types/proxyquire": "^1.3.28",
73-
"@types/rate-limit-redis": "^1.7.4",
74-
"@typescript-eslint/eslint-plugin": "^7.16.0",
75-
"@typescript-eslint/parser": "^7.16.0",
66+
"@semantic-release/github": "^12.0.6",
67+
"@semantic-release/npm": "^13.1.5",
68+
"@semantic-release/release-notes-generator": "^14.1.0",
69+
"@types/express-rate-limit": "^6.0.2",
70+
"@types/ioredis": "^4.28.10",
71+
"@types/memcached": "^2.2.10",
72+
"@types/node": "^25.5.0",
73+
"@types/proxyquire": "^1.3.31",
74+
"@types/rate-limit-redis": "^3.0.0",
75+
"@typescript-eslint/eslint-plugin": "^7.18.0",
76+
"@typescript-eslint/parser": "^7.18.0",
7677
"cz-conventional-changelog": "^3.3.0",
77-
"cz-customizable": "^6.3.0",
78-
"eslint": "^8.57.0",
79-
"eslint-config-prettier": "^9.1.0",
78+
"cz-customizable": "^7.5.1",
79+
"eslint": "^8.57.1",
80+
"eslint-config-prettier": "^10.1.8",
8081
"eslint-plugin-eslint-plugin": "^5.5.1",
81-
"eslint-plugin-mocha": "^10.4.3",
82-
"fs-extra": "^11.2.0",
82+
"eslint-plugin-mocha": "^10.5.0",
83+
"fs-extra": "^11.3.4",
8384
"git-release-notes": "^5.0.0",
84-
"husky": "^7.0.4",
85-
"jsdom": "^21.0.0",
85+
"husky": "^9.1.7",
86+
"jsdom": "^29.0.0",
8687
"loopback-connector-kv-redis": "^4.0.0",
8788
"memcached": "^2.2.2",
8889
"proxyquire": "^2.1.3",
89-
"semantic-release": "^25.0.1",
90-
"simple-git": "^3.15.1",
90+
"semantic-release": "^25.0.3",
91+
"simple-git": "^3.33.0",
9192
"source-map-support": "^0.5.21",
92-
"typescript": "~5.2.2"
93+
"typescript": "~5.5.4"
9394
},
9495
"overrides": {
9596
"peerDependencies": {
@@ -102,7 +103,10 @@
102103
"git-release-notes": {
103104
"ejs": "^3.1.8",
104105
"yargs": "^17.6.2"
105-
}
106+
},
107+
"undici": "^6.24.0",
108+
"handlebars": "^4.7.9",
109+
"lodash": "^4.18.0"
106110
},
107111
"publishConfig": {
108112
"registry": "https://registry.npmjs.org/"

src/__tests__/acceptance/ratelimit-action-acceptance/fixtures/application.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import path from 'path';
77
import {MySequence} from './sequence';
88
import {RateLimiterComponent, RateLimitSecurityBindings} from '../../../..';
99

10-
import {StoreProvider} from '../../store.provider';
1110
import {TestController} from '../../test.controller';
1211
export {ApplicationConfig};
1312
export class TestApplication extends BootMixin(
@@ -23,9 +22,7 @@ export class TestApplication extends BootMixin(
2322

2423
this.projectRoot = __dirname;
2524
this.controller(TestController);
26-
this.bind(RateLimitSecurityBindings.DATASOURCEPROVIDER).toProvider(
27-
StoreProvider,
28-
);
25+
this.bind(RateLimitSecurityBindings.DATASOURCEPROVIDER).to(null);
2926

3027
this.bind(RateLimitSecurityBindings.CONFIG).to({
3128
name: 'inMemory',

src/__tests__/acceptance/ratelimit-action-acceptance/rate-limiter.acceptance.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Client} from '@loopback/testlab';
2-
import {memoryStore} from '../store.provider';
32
import {TestApplication} from './fixtures/application';
43
import {setUpApplication} from './helper';
4+
import {clearRateLimitCache} from '../../../providers/ratelimit-action.provider';
55

66
const OK_STATUS_CODE = 200;
77
const TOO_MANY_REQS_CODE = 429;
@@ -14,7 +14,7 @@ describe('Acceptance Test Cases', () => {
1414
({app, client} = await setUpApplication());
1515
});
1616
afterEach(async () => {
17-
await clearStore();
17+
clearRateLimitCache();
1818
});
1919

2020
after(async () => app.stop());
@@ -64,8 +64,4 @@ describe('Acceptance Test Cases', () => {
6464
);
6565
}, TIMEOUT);
6666
});
67-
68-
async function clearStore() {
69-
memoryStore.resetAll();
70-
}
7167
});

src/__tests__/acceptance/ratelimit-middleware-acceptance/fixtures/application.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {RateLimiterComponent, RateLimitSecurityBindings} from '../../../..';
88
import {TestController} from '../../test.controller';
99

1010
import {MySequence} from './middleware.sequence';
11-
import {StoreProvider} from '../../store.provider';
1211
export {ApplicationConfig};
1312
export class TestApplication extends BootMixin(
1413
ServiceMixin(RepositoryMixin(RestApplication)),
@@ -26,9 +25,7 @@ export class TestApplication extends BootMixin(
2625

2726
this.projectRoot = __dirname;
2827
this.controller(TestController);
29-
this.bind(RateLimitSecurityBindings.DATASOURCEPROVIDER).toProvider(
30-
StoreProvider,
31-
);
28+
this.bind(RateLimitSecurityBindings.DATASOURCEPROVIDER).to(null);
3229

3330
this.bind(RateLimitSecurityBindings.CONFIG).to({
3431
name: 'inMemory',

src/__tests__/acceptance/ratelimit-middleware-acceptance/rate-limiter.acceptance.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {Client} from '@loopback/testlab';
2-
import {memoryStore} from '../store.provider';
32
import {TestApplication} from './fixtures/application';
43
import {setUpApplication} from './helper';
4+
import {clearRateLimitCache} from '../../../middleware/ratelimit.middleware';
5+
56
describe('Acceptance Test Cases', () => {
67
let app: TestApplication;
78
let client: Client;
@@ -10,7 +11,7 @@ describe('Acceptance Test Cases', () => {
1011
({app, client} = await setUpApplication());
1112
});
1213
afterEach(async () => {
13-
await clearStore();
14+
clearRateLimitCache();
1415
});
1516

1617
after(async () => app.stop());
@@ -58,8 +59,4 @@ describe('Acceptance Test Cases', () => {
5859
);
5960
}, 2000);
6061
});
61-
62-
async function clearStore() {
63-
memoryStore.resetAll();
64-
}
6562
});

src/__tests__/acceptance/store.provider.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/middleware/ratelimit.middleware.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@ import * as RateLimit from 'express-rate-limit';
1616
import {RateLimitSecurityBindings} from '../keys';
1717
import {RateLimitMetadata, RateLimitOptions} from '../types';
1818
import {RatelimitActionMiddlewareGroup} from './middleware.enum';
19+
20+
// Cache for RateLimit instances to avoid store reuse error in v8
21+
const rateLimitCache = new Map<string, RateLimit.RateLimitRequestHandler>();
22+
23+
function getRateLimiterKey(opts: Partial<RateLimitOptions>): string {
24+
return JSON.stringify(opts);
25+
}
26+
27+
// Export function to clear cache for testing
28+
export function clearRateLimitCache(): void {
29+
rateLimitCache.clear();
30+
}
31+
1932
@injectable(
2033
asMiddleware({
2134
group: RatelimitActionMiddlewareGroup.RATELIMIT,
@@ -26,7 +39,7 @@ import {RatelimitActionMiddlewareGroup} from './middleware.enum';
2639
export class RatelimitMiddlewareProvider implements Provider<Middleware> {
2740
constructor(
2841
@inject.getter(RateLimitSecurityBindings.DATASOURCEPROVIDER)
29-
private readonly getDatastore: Getter<RateLimit.Store>,
42+
private readonly getDatastore: Getter<RateLimit.Store | null>,
3043
@inject.getter(RateLimitSecurityBindings.METADATA)
3144
private readonly getMetadata: Getter<RateLimitMetadata>,
3245
@inject(CoreBindings.APPLICATION_INSTANCE)
@@ -60,17 +73,44 @@ export class RatelimitMiddlewareProvider implements Provider<Middleware> {
6073
const operationMetadata = metadata ? metadata.options : {};
6174

6275
// Create options based on global config and method level config
63-
const opts = {...this.config, ...operationMetadata};
76+
const rawOpts = {...this.config, ...operationMetadata};
77+
78+
// Filter out unsupported options for express-rate-limit v8
79+
// 'name' is no longer supported in v8
80+
// 'client', 'type', 'uri', 'collectionName' are custom DataSourceConfig options
81+
/* eslint-disable @typescript-eslint/no-unused-vars */
82+
const {
83+
name,
84+
client,
85+
type,
86+
uri,
87+
collectionName,
88+
store: originalStore,
89+
...opts
90+
} = rawOpts as RateLimitOptions & {store?: unknown};
91+
/* eslint-enable @typescript-eslint/no-unused-vars */
6492

93+
// If dataStore is null or undefined, don't set the store property
94+
// express-rate-limit v8 will create its own InMemoryStore
6595
if (dataStore) {
66-
opts.store = dataStore;
96+
(opts as RateLimit.Options).store = dataStore;
6797
}
6898

6999
opts.message = new HttpErrors.TooManyRequests(
70100
opts.message?.toString() ?? 'Method rate limit reached !',
71101
);
72102

73-
const limiter = RateLimit.default(opts);
103+
// Get or create a RateLimit instance for this configuration
104+
// This avoids the "store reuse" error in express-rate-limit v8
105+
// Note: We exclude 'store' from cache key since each store instance is unique
106+
const cacheKey = getRateLimiterKey(opts);
107+
let limiter = rateLimitCache.get(cacheKey);
108+
109+
if (!limiter) {
110+
limiter = RateLimit.default(opts);
111+
rateLimitCache.set(cacheKey, limiter);
112+
}
113+
74114
limiter(request, response, (err: unknown) => {
75115
if (err) {
76116
reject(err);

src/providers/ratelimit-action.provider.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,22 @@ import * as RateLimit from 'express-rate-limit';
66
import {RateLimitSecurityBindings} from '../keys';
77
import {RateLimitAction, RateLimitMetadata, RateLimitOptions} from '../types';
88

9+
// Cache for RateLimit instances to avoid store reuse error in v8
10+
const rateLimitCache = new Map<string, RateLimit.RateLimitRequestHandler>();
11+
12+
function getRateLimiterKey(opts: Partial<RateLimitOptions>): string {
13+
return JSON.stringify(opts);
14+
}
15+
16+
// Export function to clear cache for testing
17+
export function clearRateLimitCache(): void {
18+
rateLimitCache.clear();
19+
}
20+
921
export class RatelimitActionProvider implements Provider<RateLimitAction> {
1022
constructor(
1123
@inject.getter(RateLimitSecurityBindings.DATASOURCEPROVIDER)
12-
private readonly getDatastore: Getter<RateLimit.Store>,
24+
private readonly getDatastore: Getter<RateLimit.Store | null>,
1325
@inject.getter(RateLimitSecurityBindings.METADATA)
1426
private readonly getMetadata: Getter<RateLimitMetadata>,
1527
@inject(CoreBindings.APPLICATION_INSTANCE)
@@ -39,17 +51,44 @@ export class RatelimitActionProvider implements Provider<RateLimitAction> {
3951
const operationMetadata = metadata ? metadata.options : {};
4052

4153
// Create options based on global config and method level config
42-
const opts = {...this.config, ...operationMetadata};
54+
const rawOpts = {...this.config, ...operationMetadata};
4355

56+
// Filter out unsupported options for express-rate-limit v8
57+
// 'name' is no longer supported in v8
58+
// 'client', 'type', 'uri', 'collectionName' are custom DataSourceConfig options
59+
/* eslint-disable @typescript-eslint/no-unused-vars */
60+
const {
61+
name,
62+
client,
63+
type,
64+
uri,
65+
collectionName,
66+
store: originalStore,
67+
...opts
68+
} = rawOpts as RateLimitOptions & {store?: unknown};
69+
/* eslint-enable @typescript-eslint/no-unused-vars */
70+
71+
// If dataStore is null or undefined, don't set the store property
72+
// express-rate-limit v8 will create its own InMemoryStore
4473
if (dataStore) {
45-
opts.store = dataStore;
74+
(opts as RateLimit.Options).store = dataStore;
4675
}
4776

4877
opts.message = new HttpErrors.TooManyRequests(
4978
opts.message?.toString() ?? 'Method rate limit reached !',
5079
);
5180

52-
const limiter = RateLimit.default(opts);
81+
// Get or create a RateLimit instance for this configuration
82+
// This avoids the "store reuse" error in express-rate-limit v8
83+
// Note: We exclude 'store' from cache key since each store instance is unique
84+
const cacheKey = getRateLimiterKey(opts);
85+
let limiter = rateLimitCache.get(cacheKey);
86+
87+
if (!limiter) {
88+
limiter = RateLimit.default(opts);
89+
rateLimitCache.set(cacheKey, limiter);
90+
}
91+
5392
limiter(request, response, (err: unknown) => {
5493
if (err) {
5594
reject(err);

0 commit comments

Comments
 (0)