@arcjet/decorate 1.7.0-rc.0 → 1.7.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.
- package/index.d.ts +40 -0
- package/index.js +179 -0
- package/package.json +31 -34
- package/dist/index.d.ts +0 -43
- package/dist/index.js +0 -112
package/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { ArcjetDecision } from "@arcjet/protocol";
|
|
2
|
+
interface HeaderLike {
|
|
3
|
+
get(name: string): string | null;
|
|
4
|
+
has(name: string): boolean;
|
|
5
|
+
set(name: string, value: string): void;
|
|
6
|
+
}
|
|
7
|
+
interface ResponseLike {
|
|
8
|
+
headers: HeaderLike;
|
|
9
|
+
}
|
|
10
|
+
interface OutgoingMessageLike {
|
|
11
|
+
getHeader: (name: string) => string[] | number | string | undefined;
|
|
12
|
+
headersSent: boolean;
|
|
13
|
+
hasHeader: (name: string) => boolean;
|
|
14
|
+
setHeader: (name: string, value: ReadonlyArray<string> | number | string) => unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Decorable value.
|
|
18
|
+
*
|
|
19
|
+
* Something that looks like `Headers` (Fetch),
|
|
20
|
+
* `OutgoingMessage` (Node.js), or
|
|
21
|
+
* `Response` (Fetch).
|
|
22
|
+
*/
|
|
23
|
+
export type ArcjetCanDecorate = HeaderLike | OutgoingMessageLike | ResponseLike;
|
|
24
|
+
/**
|
|
25
|
+
* Decorate something based on an Arcjet decision with rate limit headers.
|
|
26
|
+
*
|
|
27
|
+
* Sets `RateLimit-Policy` and `RateLimit` and conform to the
|
|
28
|
+
* [Rate Limit fields for
|
|
29
|
+
* HTTP](https://ietf-wg-httpapi.github.io/ratelimit-headers/draft-ietf-httpapi-ratelimit-headers.html)
|
|
30
|
+
* draft specification.
|
|
31
|
+
*
|
|
32
|
+
* @param value
|
|
33
|
+
* Decorable value.
|
|
34
|
+
* @param decision
|
|
35
|
+
* Decision from `protect()`.
|
|
36
|
+
* @returns
|
|
37
|
+
* Nothing.
|
|
38
|
+
*/
|
|
39
|
+
export declare function setRateLimitHeaders(value: ArcjetCanDecorate, decision: ArcjetDecision): void;
|
|
40
|
+
export {};
|
package/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { sprintf } from '@arcjet/sprintf';
|
|
2
|
+
import '@arcjet/protocol';
|
|
3
|
+
|
|
4
|
+
function isHeaderLike(value) {
|
|
5
|
+
if ("has" in value &&
|
|
6
|
+
typeof value.has === "function" &&
|
|
7
|
+
"get" in value &&
|
|
8
|
+
typeof value.get === "function" &&
|
|
9
|
+
"set" in value &&
|
|
10
|
+
typeof value.set === "function") {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
function isResponseLike(value) {
|
|
16
|
+
if (!("headers" in value) || typeof value.headers === "undefined") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return isHeaderLike(value.headers);
|
|
20
|
+
}
|
|
21
|
+
function isOutgoingMessageLike(response) {
|
|
22
|
+
if (!("headersSent" in response) ||
|
|
23
|
+
typeof response.headersSent !== "boolean") {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (typeof response.hasHeader !== "function") {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
if (typeof response.getHeader !== "function") {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (typeof response.setHeader !== "function") {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
function sortByLowestMax([maxA, _windowA], [maxB, _windowB]) {
|
|
38
|
+
return maxA - maxB;
|
|
39
|
+
}
|
|
40
|
+
function toPolicyString([max, window]) {
|
|
41
|
+
return `${max};w=${window}`;
|
|
42
|
+
}
|
|
43
|
+
function toLimitString({ max, remaining, reset, }) {
|
|
44
|
+
return `limit=${max}, remaining=${remaining}, reset=${reset}`;
|
|
45
|
+
}
|
|
46
|
+
function extractReason(result) {
|
|
47
|
+
return result.reason;
|
|
48
|
+
}
|
|
49
|
+
function isRateLimitReason(reason) {
|
|
50
|
+
return reason.isRateLimit();
|
|
51
|
+
}
|
|
52
|
+
function nearestLimit(current, next) {
|
|
53
|
+
if (current.remaining < next.remaining) {
|
|
54
|
+
return current;
|
|
55
|
+
}
|
|
56
|
+
if (current.remaining > next.remaining) {
|
|
57
|
+
return next;
|
|
58
|
+
}
|
|
59
|
+
// Reaching here means `remaining` is equal so prioritize closest reset
|
|
60
|
+
if (current.reset < next.reset) {
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
63
|
+
if (current.reset > next.reset) {
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
66
|
+
// Reaching here means that `remaining` and `reset` are equal, so prioritize
|
|
67
|
+
// the smallest `max`
|
|
68
|
+
if (current.max < next.max) {
|
|
69
|
+
return current;
|
|
70
|
+
}
|
|
71
|
+
// All else equal, just return the next item in the list
|
|
72
|
+
return next;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Decorate something based on an Arcjet decision with rate limit headers.
|
|
76
|
+
*
|
|
77
|
+
* Sets `RateLimit-Policy` and `RateLimit` and conform to the
|
|
78
|
+
* [Rate Limit fields for
|
|
79
|
+
* HTTP](https://ietf-wg-httpapi.github.io/ratelimit-headers/draft-ietf-httpapi-ratelimit-headers.html)
|
|
80
|
+
* draft specification.
|
|
81
|
+
*
|
|
82
|
+
* @param value
|
|
83
|
+
* Decorable value.
|
|
84
|
+
* @param decision
|
|
85
|
+
* Decision from `protect()`.
|
|
86
|
+
* @returns
|
|
87
|
+
* Nothing.
|
|
88
|
+
*/
|
|
89
|
+
function setRateLimitHeaders(value, decision) {
|
|
90
|
+
const rateLimitReasons = decision.results
|
|
91
|
+
.map(extractReason)
|
|
92
|
+
.filter(isRateLimitReason);
|
|
93
|
+
let policy;
|
|
94
|
+
let limit;
|
|
95
|
+
if (rateLimitReasons.length > 0) {
|
|
96
|
+
const policies = new Map();
|
|
97
|
+
for (const reason of rateLimitReasons) {
|
|
98
|
+
if (policies.has(reason.max)) {
|
|
99
|
+
console.error("Invalid rate limit policy—two policies should not share the same limit");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (typeof reason.max !== "number" ||
|
|
103
|
+
typeof reason.window !== "number" ||
|
|
104
|
+
typeof reason.remaining !== "number" ||
|
|
105
|
+
typeof reason.reset !== "number") {
|
|
106
|
+
console.error(sprintf("Invalid rate limit encountered: %o", reason));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
policies.set(reason.max, reason.window);
|
|
110
|
+
}
|
|
111
|
+
const rl = rateLimitReasons.reduce(nearestLimit);
|
|
112
|
+
limit = toLimitString(rl);
|
|
113
|
+
policy = Array.from(policies.entries())
|
|
114
|
+
.sort(sortByLowestMax)
|
|
115
|
+
.map(toPolicyString)
|
|
116
|
+
.join(", ");
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// For cached decisions, we may not have rule results, but we'd still have
|
|
120
|
+
// the top-level reason.
|
|
121
|
+
if (isRateLimitReason(decision.reason)) {
|
|
122
|
+
if (typeof decision.reason.max !== "number" ||
|
|
123
|
+
typeof decision.reason.window !== "number" ||
|
|
124
|
+
typeof decision.reason.remaining !== "number" ||
|
|
125
|
+
typeof decision.reason.reset !== "number") {
|
|
126
|
+
console.error(sprintf("Invalid rate limit encountered: %o", decision.reason));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
limit = toLimitString(decision.reason);
|
|
130
|
+
policy = toPolicyString([decision.reason.max, decision.reason.window]);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (isHeaderLike(value)) {
|
|
137
|
+
if (value.has("RateLimit")) {
|
|
138
|
+
console.warn(sprintf("Response already contains `RateLimit` header\n Original: %s\n New: %s", value.get("RateLimit"), limit));
|
|
139
|
+
}
|
|
140
|
+
if (value.has("RateLimit-Policy")) {
|
|
141
|
+
console.warn(sprintf("Response already contains `RateLimit-Policy` header\n Original: %s\n New: %s", value.get("RateLimit-Policy"), limit));
|
|
142
|
+
}
|
|
143
|
+
value.set("RateLimit", limit);
|
|
144
|
+
value.set("RateLimit-Policy", policy);
|
|
145
|
+
// The response was handled
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (isResponseLike(value)) {
|
|
149
|
+
if (value.headers.has("RateLimit")) {
|
|
150
|
+
console.warn(sprintf("Response already contains `RateLimit` header\n Original: %s\n New: %s", value.headers.get("RateLimit"), limit));
|
|
151
|
+
}
|
|
152
|
+
if (value.headers.has("RateLimit-Policy")) {
|
|
153
|
+
console.warn(sprintf("Response already contains `RateLimit-Policy` header\n Original: %s\n New: %s", value.headers.get("RateLimit-Policy"), limit));
|
|
154
|
+
}
|
|
155
|
+
value.headers.set("RateLimit", limit);
|
|
156
|
+
value.headers.set("RateLimit-Policy", policy);
|
|
157
|
+
// The response was handled
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (isOutgoingMessageLike(value)) {
|
|
161
|
+
if (value.headersSent) {
|
|
162
|
+
console.error("Headers have already been sent—cannot set RateLimit header");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (value.hasHeader("RateLimit")) {
|
|
166
|
+
console.warn(sprintf("Response already contains `RateLimit` header\n Original: %s\n New: %s", value.getHeader("RateLimit"), limit));
|
|
167
|
+
}
|
|
168
|
+
if (value.hasHeader("RateLimit-Policy")) {
|
|
169
|
+
console.warn(sprintf("Response already contains `RateLimit-Policy` header\n Original: %s\n New: %s", value.getHeader("RateLimit-Policy"), limit));
|
|
170
|
+
}
|
|
171
|
+
value.setHeader("RateLimit", limit);
|
|
172
|
+
value.setHeader("RateLimit-Policy", policy);
|
|
173
|
+
// The response was handled
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
console.debug("Cannot determine if response is Response or OutgoingMessage type");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export { setRateLimitHeaders };
|
package/package.json
CHANGED
|
@@ -1,63 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcjet/decorate",
|
|
3
|
-
"version": "1.7.0
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Arcjet utilities for decorating responses with information",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arcjet",
|
|
7
7
|
"decore",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"utility",
|
|
9
|
+
"util"
|
|
10
10
|
],
|
|
11
|
+
"license": "Apache-2.0",
|
|
11
12
|
"homepage": "https://arcjet.com",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/arcjet/arcjet-js.git",
|
|
16
|
+
"directory": "decorate"
|
|
17
|
+
},
|
|
12
18
|
"bugs": {
|
|
13
19
|
"url": "https://github.com/arcjet/arcjet-js/issues",
|
|
14
20
|
"email": "support@arcjet.com"
|
|
15
21
|
},
|
|
16
|
-
"license": "Apache-2.0",
|
|
17
22
|
"author": {
|
|
18
23
|
"name": "Arcjet",
|
|
19
24
|
"email": "support@arcjet.com",
|
|
20
25
|
"url": "https://arcjet.com"
|
|
21
26
|
},
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"url": "git+https://github.com/arcjet/arcjet-js.git",
|
|
25
|
-
"directory": "decorate"
|
|
27
|
+
"engines": {
|
|
28
|
+
"node": ">=22.21.0 <23 || >=24.5.0"
|
|
26
29
|
},
|
|
30
|
+
"type": "module",
|
|
31
|
+
"main": "./index.js",
|
|
32
|
+
"types": "./index.d.ts",
|
|
27
33
|
"files": [
|
|
28
|
-
"
|
|
34
|
+
"index.d.ts",
|
|
35
|
+
"index.js"
|
|
29
36
|
],
|
|
30
|
-
"type": "module",
|
|
31
|
-
"main": "./dist/index.js",
|
|
32
|
-
"types": "./dist/index.d.ts",
|
|
33
|
-
"exports": {
|
|
34
|
-
".": {
|
|
35
|
-
"types": "./dist/index.d.ts",
|
|
36
|
-
"default": "./dist/index.js"
|
|
37
|
-
},
|
|
38
|
-
"./package.json": "./package.json"
|
|
39
|
-
},
|
|
40
|
-
"publishConfig": {
|
|
41
|
-
"access": "public",
|
|
42
|
-
"tag": "latest"
|
|
43
|
-
},
|
|
44
37
|
"scripts": {
|
|
45
|
-
"build": "
|
|
46
|
-
"
|
|
47
|
-
"test-api": "node --test -- test/*.test.
|
|
48
|
-
"test-coverage": "node --experimental-test-coverage --test -- test/*.test.
|
|
49
|
-
"test": "npm run build && npm run test-coverage"
|
|
38
|
+
"build": "rollup --config rollup.config.js",
|
|
39
|
+
"lint": "eslint .",
|
|
40
|
+
"test-api": "node --test -- test/*.test.js",
|
|
41
|
+
"test-coverage": "node --experimental-test-coverage --test -- test/*.test.js",
|
|
42
|
+
"test": "npm run build && npm run lint && npm run test-coverage"
|
|
50
43
|
},
|
|
51
44
|
"dependencies": {
|
|
52
|
-
"@arcjet/protocol": "1.7.0
|
|
53
|
-
"@arcjet/sprintf": "1.7.0
|
|
45
|
+
"@arcjet/protocol": "1.7.0",
|
|
46
|
+
"@arcjet/sprintf": "1.7.0"
|
|
54
47
|
},
|
|
55
48
|
"devDependencies": {
|
|
49
|
+
"@arcjet/eslint-config": "1.7.0",
|
|
50
|
+
"@arcjet/rollup-config": "1.7.0",
|
|
51
|
+
"@rollup/wasm-node": "4.62.2",
|
|
56
52
|
"@types/node": "22.19.21",
|
|
57
|
-
"
|
|
58
|
-
"typescript": "
|
|
53
|
+
"eslint": "9.39.4",
|
|
54
|
+
"typescript": "5.9.3"
|
|
59
55
|
},
|
|
60
|
-
"
|
|
61
|
-
"
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"tag": "latest"
|
|
62
59
|
}
|
|
63
60
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { ArcjetDecision } from "@arcjet/protocol";
|
|
2
|
-
|
|
3
|
-
//#region src/index.d.ts
|
|
4
|
-
interface HeaderLike {
|
|
5
|
-
get(name: string): string | null;
|
|
6
|
-
has(name: string): boolean;
|
|
7
|
-
set(name: string, value: string): void;
|
|
8
|
-
}
|
|
9
|
-
interface ResponseLike {
|
|
10
|
-
headers: HeaderLike;
|
|
11
|
-
}
|
|
12
|
-
interface OutgoingMessageLike {
|
|
13
|
-
getHeader: (name: string) => string[] | number | string | undefined;
|
|
14
|
-
headersSent: boolean;
|
|
15
|
-
hasHeader: (name: string) => boolean;
|
|
16
|
-
setHeader: (name: string, value: ReadonlyArray<string> | number | string) => unknown;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Decorable value.
|
|
20
|
-
*
|
|
21
|
-
* Something that looks like `Headers` (Fetch),
|
|
22
|
-
* `OutgoingMessage` (Node.js), or
|
|
23
|
-
* `Response` (Fetch).
|
|
24
|
-
*/
|
|
25
|
-
type ArcjetCanDecorate = HeaderLike | OutgoingMessageLike | ResponseLike;
|
|
26
|
-
/**
|
|
27
|
-
* Decorate something based on an Arcjet decision with rate limit headers.
|
|
28
|
-
*
|
|
29
|
-
* Sets `RateLimit-Policy` and `RateLimit` and conform to the
|
|
30
|
-
* [Rate Limit fields for
|
|
31
|
-
* HTTP](https://ietf-wg-httpapi.github.io/ratelimit-headers/draft-ietf-httpapi-ratelimit-headers.html)
|
|
32
|
-
* draft specification.
|
|
33
|
-
*
|
|
34
|
-
* @param value
|
|
35
|
-
* Decorable value.
|
|
36
|
-
* @param decision
|
|
37
|
-
* Decision from `protect()`.
|
|
38
|
-
* @returns
|
|
39
|
-
* Nothing.
|
|
40
|
-
*/
|
|
41
|
-
declare function setRateLimitHeaders(value: ArcjetCanDecorate, decision: ArcjetDecision): void;
|
|
42
|
-
//#endregion
|
|
43
|
-
export { ArcjetCanDecorate, setRateLimitHeaders };
|
package/dist/index.js
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import "@arcjet/protocol";
|
|
2
|
-
import { sprintf } from "@arcjet/sprintf";
|
|
3
|
-
//#region src/index.ts
|
|
4
|
-
function isHeaderLike(value) {
|
|
5
|
-
if ("has" in value && typeof value.has === "function" && "get" in value && typeof value.get === "function" && "set" in value && typeof value.set === "function") return true;
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
function isResponseLike(value) {
|
|
9
|
-
if (!("headers" in value) || typeof value.headers === "undefined") return false;
|
|
10
|
-
return isHeaderLike(value.headers);
|
|
11
|
-
}
|
|
12
|
-
function isOutgoingMessageLike(response) {
|
|
13
|
-
if (!("headersSent" in response) || typeof response.headersSent !== "boolean") return false;
|
|
14
|
-
if (typeof response.hasHeader !== "function") return false;
|
|
15
|
-
if (typeof response.getHeader !== "function") return false;
|
|
16
|
-
if (typeof response.setHeader !== "function") return false;
|
|
17
|
-
return true;
|
|
18
|
-
}
|
|
19
|
-
function sortByLowestMax([maxA, _windowA], [maxB, _windowB]) {
|
|
20
|
-
return maxA - maxB;
|
|
21
|
-
}
|
|
22
|
-
function toPolicyString([max, window]) {
|
|
23
|
-
return `${max};w=${window}`;
|
|
24
|
-
}
|
|
25
|
-
function toLimitString({ max, remaining, reset }) {
|
|
26
|
-
return `limit=${max}, remaining=${remaining}, reset=${reset}`;
|
|
27
|
-
}
|
|
28
|
-
function extractReason(result) {
|
|
29
|
-
return result.reason;
|
|
30
|
-
}
|
|
31
|
-
function isRateLimitReason(reason) {
|
|
32
|
-
return reason.isRateLimit();
|
|
33
|
-
}
|
|
34
|
-
function nearestLimit(current, next) {
|
|
35
|
-
if (current.remaining < next.remaining) return current;
|
|
36
|
-
if (current.remaining > next.remaining) return next;
|
|
37
|
-
if (current.reset < next.reset) return current;
|
|
38
|
-
if (current.reset > next.reset) return next;
|
|
39
|
-
if (current.max < next.max) return current;
|
|
40
|
-
return next;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Decorate something based on an Arcjet decision with rate limit headers.
|
|
44
|
-
*
|
|
45
|
-
* Sets `RateLimit-Policy` and `RateLimit` and conform to the
|
|
46
|
-
* [Rate Limit fields for
|
|
47
|
-
* HTTP](https://ietf-wg-httpapi.github.io/ratelimit-headers/draft-ietf-httpapi-ratelimit-headers.html)
|
|
48
|
-
* draft specification.
|
|
49
|
-
*
|
|
50
|
-
* @param value
|
|
51
|
-
* Decorable value.
|
|
52
|
-
* @param decision
|
|
53
|
-
* Decision from `protect()`.
|
|
54
|
-
* @returns
|
|
55
|
-
* Nothing.
|
|
56
|
-
*/
|
|
57
|
-
function setRateLimitHeaders(value, decision) {
|
|
58
|
-
const rateLimitReasons = decision.results.map(extractReason).filter(isRateLimitReason);
|
|
59
|
-
let policy;
|
|
60
|
-
let limit;
|
|
61
|
-
if (rateLimitReasons.length > 0) {
|
|
62
|
-
const policies = /* @__PURE__ */ new Map();
|
|
63
|
-
for (const reason of rateLimitReasons) {
|
|
64
|
-
if (policies.has(reason.max)) {
|
|
65
|
-
console.error("Invalid rate limit policy—two policies should not share the same limit");
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
if (typeof reason.max !== "number" || typeof reason.window !== "number" || typeof reason.remaining !== "number" || typeof reason.reset !== "number") {
|
|
69
|
-
console.error(sprintf("Invalid rate limit encountered: %o", reason));
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
policies.set(reason.max, reason.window);
|
|
73
|
-
}
|
|
74
|
-
limit = toLimitString(rateLimitReasons.reduce(nearestLimit));
|
|
75
|
-
policy = Array.from(policies.entries()).sort(sortByLowestMax).map(toPolicyString).join(", ");
|
|
76
|
-
} else if (isRateLimitReason(decision.reason)) {
|
|
77
|
-
if (typeof decision.reason.max !== "number" || typeof decision.reason.window !== "number" || typeof decision.reason.remaining !== "number" || typeof decision.reason.reset !== "number") {
|
|
78
|
-
console.error(sprintf("Invalid rate limit encountered: %o", decision.reason));
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
limit = toLimitString(decision.reason);
|
|
82
|
-
policy = toPolicyString([decision.reason.max, decision.reason.window]);
|
|
83
|
-
} else return;
|
|
84
|
-
if (isHeaderLike(value)) {
|
|
85
|
-
if (value.has("RateLimit")) console.warn(sprintf("Response already contains `RateLimit` header\n Original: %s\n New: %s", value.get("RateLimit"), limit));
|
|
86
|
-
if (value.has("RateLimit-Policy")) console.warn(sprintf("Response already contains `RateLimit-Policy` header\n Original: %s\n New: %s", value.get("RateLimit-Policy"), limit));
|
|
87
|
-
value.set("RateLimit", limit);
|
|
88
|
-
value.set("RateLimit-Policy", policy);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
if (isResponseLike(value)) {
|
|
92
|
-
if (value.headers.has("RateLimit")) console.warn(sprintf("Response already contains `RateLimit` header\n Original: %s\n New: %s", value.headers.get("RateLimit"), limit));
|
|
93
|
-
if (value.headers.has("RateLimit-Policy")) console.warn(sprintf("Response already contains `RateLimit-Policy` header\n Original: %s\n New: %s", value.headers.get("RateLimit-Policy"), limit));
|
|
94
|
-
value.headers.set("RateLimit", limit);
|
|
95
|
-
value.headers.set("RateLimit-Policy", policy);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (isOutgoingMessageLike(value)) {
|
|
99
|
-
if (value.headersSent) {
|
|
100
|
-
console.error("Headers have already been sent—cannot set RateLimit header");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (value.hasHeader("RateLimit")) console.warn(sprintf("Response already contains `RateLimit` header\n Original: %s\n New: %s", value.getHeader("RateLimit"), limit));
|
|
104
|
-
if (value.hasHeader("RateLimit-Policy")) console.warn(sprintf("Response already contains `RateLimit-Policy` header\n Original: %s\n New: %s", value.getHeader("RateLimit-Policy"), limit));
|
|
105
|
-
value.setHeader("RateLimit", limit);
|
|
106
|
-
value.setHeader("RateLimit-Policy", policy);
|
|
107
|
-
return;
|
|
108
|
-
}
|
|
109
|
-
console.debug("Cannot determine if response is Response or OutgoingMessage type");
|
|
110
|
-
}
|
|
111
|
-
//#endregion
|
|
112
|
-
export { setRateLimitHeaders };
|