@forestadmin/agent-bff 1.23.7 → 1.24.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 (35) hide show
  1. package/README.md +42 -31
  2. package/dist/cli-core.js +9 -5
  3. package/dist/cli-dispatch.js +4 -3
  4. package/dist/config/env-config.d.ts +8 -0
  5. package/dist/config/env-config.js +50 -18
  6. package/dist/cors/origin.d.ts +1 -0
  7. package/dist/cors/origin.js +7 -3
  8. package/dist/cors/per-key-origin.js +3 -2
  9. package/dist/data/request-schemas.d.ts +1 -0
  10. package/dist/data/request-schemas.js +4 -3
  11. package/dist/docs/docs-samples.d.ts +3 -2
  12. package/dist/docs/docs-samples.js +4 -3
  13. package/dist/http/bff-local-errors.d.ts +6 -0
  14. package/dist/http/bff-local-errors.js +13 -1
  15. package/dist/openapi/collect-unfolding.js +2 -2
  16. package/dist/openapi/names.d.ts +2 -0
  17. package/dist/openapi/names.js +6 -1
  18. package/dist/openapi/openapi-document.d.ts +3 -1
  19. package/dist/openapi/openapi-document.js +43 -10
  20. package/dist/openapi/openapi-routes.d.ts +2 -1
  21. package/dist/openapi/openapi-routes.js +4 -3
  22. package/dist/openapi/record-schemas.d.ts +21 -0
  23. package/dist/openapi/record-schemas.js +149 -0
  24. package/dist/openapi/schemas.d.ts +4 -0
  25. package/dist/openapi/schemas.js +15 -8
  26. package/dist/openapi/unfolded-document.d.ts +2 -1
  27. package/dist/openapi/unfolded-document.js +3 -3
  28. package/dist/openapi/unfolded-paths.js +74 -30
  29. package/dist/openapi/unfolding.d.ts +9 -1
  30. package/dist/openapi/unfolding.js +1 -1
  31. package/dist/rate-limit/agent-path.d.ts +2 -0
  32. package/dist/rate-limit/agent-path.js +7 -0
  33. package/dist/rate-limit/rate-limit-middleware.d.ts +9 -0
  34. package/dist/rate-limit/rate-limit-middleware.js +61 -0
  35. package/package.json +5 -3
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.default = createRateLimitMiddleware;
7
+ const agent_path_1 = __importDefault(require("./agent-path"));
8
+ const bff_http_error_1 = require("../http/bff-http-error");
9
+ const bff_local_errors_1 = require("../http/bff-local-errors");
10
+ const DEFAULT_MAX_ENTRIES = 10000;
11
+ const MS_PER_SECOND = 1000;
12
+ function retryAfterSeconds(resetAt, current) {
13
+ return Math.max(1, Math.ceil((resetAt - current) / MS_PER_SECOND));
14
+ }
15
+ function bucketKeyOf(state) {
16
+ const { apiKeyIdentity, principal } = state;
17
+ if (apiKeyIdentity)
18
+ return `api-key:${apiKeyIdentity.renderingId}:${apiKeyIdentity.user.id}`;
19
+ if (principal)
20
+ return `oauth:${principal.rendering_id}:${principal.id}`;
21
+ return undefined;
22
+ }
23
+ function createRateLimitMiddleware({ windowMs, maxRequests, now = Date.now, maxEntries = DEFAULT_MAX_ENTRIES, }) {
24
+ const buckets = new Map();
25
+ function purgeExpired(current) {
26
+ for (const [key, entry] of buckets) {
27
+ if (current >= entry.resetAt)
28
+ buckets.delete(key);
29
+ }
30
+ }
31
+ return async function rateLimitMiddleware(ctx, next) {
32
+ if (!(0, agent_path_1.default)(ctx.path)) {
33
+ await next();
34
+ return;
35
+ }
36
+ const key = bucketKeyOf(ctx.state);
37
+ if (key === undefined)
38
+ throw (0, bff_http_error_1.unauthorized)('No caller identity for this request');
39
+ const current = now();
40
+ let entry = buckets.get(key);
41
+ if (entry && current >= entry.resetAt) {
42
+ buckets.delete(key);
43
+ entry = undefined;
44
+ }
45
+ if (entry === undefined) {
46
+ purgeExpired(current);
47
+ if (buckets.size >= maxEntries) {
48
+ const earliestReset = Math.min(...[...buckets.values()].map(live => live.resetAt));
49
+ throw (0, bff_local_errors_1.tooManyRequests)(retryAfterSeconds(earliestReset, current), 'Too many requests: the rate limiter is saturated', bff_local_errors_1.RATE_LIMIT_CAUSES.limiterSaturated);
50
+ }
51
+ entry = { count: 0, resetAt: current + windowMs };
52
+ buckets.set(key, entry);
53
+ }
54
+ entry.count += 1;
55
+ if (entry.count > maxRequests) {
56
+ throw (0, bff_local_errors_1.tooManyRequests)(retryAfterSeconds(entry.resetAt, current), `Too many requests: the limit is ${maxRequests} per ${Math.round(windowMs / MS_PER_SECOND)}s per identity`, bff_local_errors_1.RATE_LIMIT_CAUSES.limitExceeded);
57
+ }
58
+ await next();
59
+ };
60
+ }
61
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZS1saW1pdC1taWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL3JhdGUtbGltaXQvcmF0ZS1saW1pdC1taWRkbGV3YXJlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBMENBLDRDQWdFQztBQXRHRCw4REFBdUM7QUFDdkMsMkRBQXNEO0FBQ3RELCtEQUE4RTtBQWM5RSxNQUFNLG1CQUFtQixHQUFHLEtBQU0sQ0FBQztBQUVuQyxNQUFNLGFBQWEsR0FBRyxJQUFJLENBQUM7QUFFM0IsU0FBUyxpQkFBaUIsQ0FBQyxPQUFlLEVBQUUsT0FBZTtJQUN6RCxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDLEdBQUcsYUFBYSxDQUFDLENBQUMsQ0FBQztBQUNyRSxDQUFDO0FBT0QsU0FBUyxXQUFXLENBQUMsS0FBb0I7SUFDdkMsTUFBTSxFQUFFLGNBQWMsRUFBRSxTQUFTLEVBQUUsR0FBRyxLQUFLLENBQUM7SUFFNUMsSUFBSSxjQUFjO1FBQUUsT0FBTyxXQUFXLGNBQWMsQ0FBQyxXQUFXLElBQUksY0FBYyxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztJQUM3RixJQUFJLFNBQVM7UUFBRSxPQUFPLFNBQVMsU0FBUyxDQUFDLFlBQVksSUFBSSxTQUFTLENBQUMsRUFBRSxFQUFFLENBQUM7SUFFeEUsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQztBQUVELFNBQXdCLHlCQUF5QixDQUFDLEVBQ2hELFFBQVEsRUFDUixXQUFXLEVBQ1gsR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQ2QsVUFBVSxHQUFHLG1CQUFtQixHQUNMO0lBQzNCLE1BQU0sT0FBTyxHQUFHLElBQUksR0FBRyxFQUF1QixDQUFDO0lBRS9DLFNBQVMsWUFBWSxDQUFDLE9BQWU7UUFDbkMsS0FBSyxNQUFNLENBQUMsR0FBRyxFQUFFLEtBQUssQ0FBQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ25DLElBQUksT0FBTyxJQUFJLEtBQUssQ0FBQyxPQUFPO2dCQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDcEQsQ0FBQztJQUNILENBQUM7SUFFRCxPQUFPLEtBQUssVUFBVSxtQkFBbUIsQ0FBQyxHQUFHLEVBQUUsSUFBSTtRQUNqRCxJQUFJLENBQUMsSUFBQSxvQkFBVyxFQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQzNCLE1BQU0sSUFBSSxFQUFFLENBQUM7WUFFYixPQUFPO1FBQ1QsQ0FBQztRQUVELE1BQU0sR0FBRyxHQUFHLFdBQVcsQ0FBQyxHQUFHLENBQUMsS0FBc0IsQ0FBQyxDQUFDO1FBRXBELElBQUksR0FBRyxLQUFLLFNBQVM7WUFBRSxNQUFNLElBQUEsNkJBQVksRUFBQyxxQ0FBcUMsQ0FBQyxDQUFDO1FBRWpGLE1BQU0sT0FBTyxHQUFHLEdBQUcsRUFBRSxDQUFDO1FBQ3RCLElBQUksS0FBSyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFN0IsSUFBSSxLQUFLLElBQUksT0FBTyxJQUFJLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQztZQUN0QyxPQUFPLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1lBQ3BCLEtBQUssR0FBRyxTQUFTLENBQUM7UUFDcEIsQ0FBQztRQUVELElBQUksS0FBSyxLQUFLLFNBQVMsRUFBRSxDQUFDO1lBQ3hCLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUV0QixJQUFJLE9BQU8sQ0FBQyxJQUFJLElBQUksVUFBVSxFQUFFLENBQUM7Z0JBQy9CLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEdBQUcsT0FBTyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7Z0JBRW5GLE1BQU0sSUFBQSxrQ0FBZSxFQUNuQixpQkFBaUIsQ0FBQyxhQUFhLEVBQUUsT0FBTyxDQUFDLEVBQ3pDLGtEQUFrRCxFQUNsRCxvQ0FBaUIsQ0FBQyxnQkFBZ0IsQ0FDbkMsQ0FBQztZQUNKLENBQUM7WUFFRCxLQUFLLEdBQUcsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxPQUFPLEdBQUcsUUFBUSxFQUFFLENBQUM7WUFDbEQsT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFDMUIsQ0FBQztRQUVELEtBQUssQ0FBQyxLQUFLLElBQUksQ0FBQyxDQUFDO1FBRWpCLElBQUksS0FBSyxDQUFDLEtBQUssR0FBRyxXQUFXLEVBQUUsQ0FBQztZQUM5QixNQUFNLElBQUEsa0NBQWUsRUFDbkIsaUJBQWlCLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxPQUFPLENBQUMsRUFDekMsbUNBQW1DLFdBQVcsUUFBUSxJQUFJLENBQUMsS0FBSyxDQUM5RCxRQUFRLEdBQUcsYUFBYSxDQUN6QixnQkFBZ0IsRUFDakIsb0NBQWlCLENBQUMsYUFBYSxDQUNoQyxDQUFDO1FBQ0osQ0FBQztRQUVELE1BQU0sSUFBSSxFQUFFLENBQUM7SUFDZixDQUFDLENBQUM7QUFDSixDQUFDIn0=
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forestadmin/agent-bff",
3
- "version": "1.23.7",
3
+ "version": "1.24.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "forest-bff": "dist/cli.js"
@@ -37,16 +37,18 @@
37
37
  "@forestadmin/datasource-toolkit": "1.55.1",
38
38
  "@forestadmin/forestadmin-client": "1.43.3",
39
39
  "@koa/bodyparser": "^6.1.0",
40
+ "inflected": "^1.1.6",
40
41
  "jsonwebtoken": "^9.0.3",
41
42
  "koa": "^3.0.1",
42
43
  "sanitize-html": "2.17.5",
43
44
  "zod": "4.3.6"
44
45
  },
45
46
  "devDependencies": {
46
- "@forestadmin/agent": "1.98.6",
47
- "@forestadmin/agent-testing": "1.2.10",
47
+ "@forestadmin/agent": "1.98.8",
48
+ "@forestadmin/agent-testing": "1.2.12",
48
49
  "@hey-api/openapi-ts": "0.99.0",
49
50
  "@redocly/cli": "2.35.1",
51
+ "@types/inflected": "^1.1.29",
50
52
  "@types/jsonwebtoken": "^9.0.1",
51
53
  "@types/koa": "^2.13.5",
52
54
  "@types/sanitize-html": "^2.16.1",