Skip to content

Commit a137b20

Browse files
committed
refc: rename and move some types around, add comments
1 parent d045b3a commit a137b20

File tree

3 files changed

+115
-49
lines changed

3 files changed

+115
-49
lines changed

source/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1+
// /source/index.ts
2+
// Export away!
3+
4+
// Export all the types as named exports
15
export * from './types.js'
6+
7+
// Export the public API as named exports too.
28
export { parseRateLimit } from './ratelimit-header-parser.js'

source/ratelimit-header-parser.ts

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,60 @@
1-
import type {
2-
ServerResponse,
3-
IncomingHttpHeaders,
4-
OutgoingHttpHeaders,
5-
} from 'node:http'
6-
import type { RateLimit, RateLimitOptions } from './types'
7-
8-
// Node or fetch
9-
export type ResponseObject = ServerResponse | Response
10-
export type HeadersObject =
11-
| IncomingHttpHeaders
12-
| OutgoingHttpHeaders
13-
| Headers
14-
| { [key: string]: string | string[] }
15-
export type ResponseOrHeadersObject = ResponseObject | HeadersObject
1+
// /source/ratelimit-header-parser.ts
2+
// The parser and associated functions
163

4+
import type {
5+
ResponseObject,
6+
HeadersObject,
7+
RateLimitInfo,
8+
ParserOptions,
9+
} from './types'
10+
11+
/**
12+
* Parses the passed response/headers object and returns rate limit information.
13+
*
14+
* @param input {ResponseObject | HeadersObject} - The node/fetch-style response/headers object.
15+
* @param passedOptions {Partial<ParserOptions> | undefined} - The configuration for the parser.
16+
*
17+
* @returns {RateLimitInfo | undefined} - The rate limit information parsed from the headers.
18+
*/
1719
export function parseRateLimit(
18-
input: ResponseOrHeadersObject,
19-
options?: RateLimitOptions,
20-
): RateLimit | undefined {
20+
input: ResponseObject | HeadersObject,
21+
passedOptions?: Partial<ParserOptions>,
22+
): RateLimitInfo | undefined {
23+
// Default to no configuration.
24+
const options = passedOptions ?? {}
25+
26+
// Get the headers object from the passed input.
27+
let headers: HeadersObject
2128
if (
2229
'headers' in input &&
2330
typeof input.headers === 'object' &&
2431
!Array.isArray(input.headers)
25-
) {
26-
return parseHeadersObject(input.headers, options)
27-
}
28-
29-
if ('getHeaders' in input && typeof input.getHeaders === 'function') {
30-
return parseHeadersObject(input.getHeaders(), options)
31-
}
32-
33-
return parseHeadersObject(input as HeadersObject, options)
32+
)
33+
headers = input.headers
34+
else if ('getHeaders' in input && typeof input.getHeaders === 'function')
35+
headers = input.getHeaders()
36+
else headers = input as HeadersObject
37+
38+
// Parse the headers.
39+
return parseHeaders(headers, options)
3440
}
3541

36-
function parseHeadersObject(
37-
input: HeadersObject,
38-
options: RateLimitOptions | undefined,
39-
): RateLimit | undefined {
40-
const combined = getHeader(input, 'ratelimit')
42+
/**
43+
* The internal parser function.
44+
*/
45+
function parseHeaders(
46+
headers: HeadersObject,
47+
options: Partial<ParserOptions>,
48+
): RateLimitInfo | undefined {
49+
const combined = getHeader(headers, 'ratelimit')
4150
if (combined) return parseCombinedRateLimitHeader(combined)
4251

4352
let prefix
44-
if (getHeader(input, 'ratelimit-remaining')) {
53+
if (getHeader(headers, 'ratelimit-remaining')) {
4554
prefix = 'ratelimit-'
46-
} else if (getHeader(input, 'x-ratelimit-remaining')) {
55+
} else if (getHeader(headers, 'x-ratelimit-remaining')) {
4756
prefix = 'x-ratelimit-'
48-
} else if (getHeader(input, 'x-rate-limit-remaining')) {
57+
} else if (getHeader(headers, 'x-rate-limit-remaining')) {
4958
// Twitter - https://developer.twitter.com/en/docs/twitter-api/rate-limits#headers-and-codes
5059
prefix = 'x-rate-limit-'
5160
} else {
@@ -57,18 +66,18 @@ function parseHeadersObject(
5766
return
5867
}
5968

60-
const limit = toInt(getHeader(input, `${prefix}limit`))
69+
const limit = toInt(getHeader(headers, `${prefix}limit`))
6170
// Used - https://github.com/reddit-archive/reddit/wiki/API#rules
6271
// used - https://docs.github.com/en/rest/overview/resources-in-the-rest-api?apiVersion=2022-11-28#rate-limit-headers
6372
// observed - https://docs.gitlab.com/ee/administration/settings/user_and_ip_rate_limits.html#response-headers
6473
// note that || is valid here because used should always be at least 1, and || handles NaN correctly, whereas ?? doesn't
6574
const used =
66-
toInt(getHeader(input, `${prefix}used`)) ||
67-
toInt(getHeader(input, `${prefix}observed`))
68-
const remaining = toInt(getHeader(input, `${prefix}remaining`))
75+
toInt(getHeader(headers, `${prefix}used`)) ||
76+
toInt(getHeader(headers, `${prefix}observed`))
77+
const remaining = toInt(getHeader(headers, `${prefix}remaining`))
6978

7079
let reset: Date | undefined
71-
const resetRaw = getHeader(input, `${prefix}reset`)
80+
const resetRaw = getHeader(headers, `${prefix}reset`)
7281
const resetType = options?.reset
7382
switch (resetType) {
7483
case 'date': {
@@ -95,7 +104,7 @@ function parseHeadersObject(
95104
if (resetRaw) reset = parseResetAuto(resetRaw)
96105
else {
97106
// Fallback to retry-after
98-
const retryAfter = getHeader(input, 'retry-after')
107+
const retryAfter = getHeader(headers, 'retry-after')
99108
if (retryAfter) {
100109
reset = parseResetUnix(retryAfter)
101110
}
@@ -114,10 +123,10 @@ function parseHeadersObject(
114123
const reLimit = /limit\s*=\s*(\d+)/i
115124
const reRemaining = /remaining\s*=\s*(\d+)/i
116125
const reReset = /reset\s*=\s*(\d+)/i
117-
export function parseCombinedRateLimitHeader(input: string): RateLimit {
118-
const limit = toInt(reLimit.exec(input)?.[1])
119-
const remaining = toInt(reRemaining.exec(input)?.[1])
120-
const resetSeconds = toInt(reReset.exec(input)?.[1])
126+
export function parseCombinedRateLimitHeader(header: string): RateLimitInfo {
127+
const limit = toInt(reLimit.exec(header)?.[1])
128+
const remaining = toInt(reRemaining.exec(header)?.[1])
129+
const resetSeconds = toInt(reReset.exec(header)?.[1])
121130
const reset = secondsToDate(resetSeconds)
122131
return {
123132
limit,

source/types.ts

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,62 @@
1-
export type RateLimit = {
1+
// /source/types.ts
2+
// All the types used by this package
3+
4+
import type {
5+
ServerResponse,
6+
IncomingHttpHeaders,
7+
OutgoingHttpHeaders,
8+
} from 'node:http'
9+
10+
/**
11+
* The parser accepts node/fetch style response and header objects.
12+
*/
13+
export type ResponseObject = ServerResponse | Response
14+
export type HeadersObject =
15+
| IncomingHttpHeaders
16+
| OutgoingHttpHeaders
17+
| Headers
18+
| { [key: string]: string | string[] }
19+
20+
/**
21+
* The rate limit information gleaned from the response/headers object passed
22+
* to the parser.
23+
*/
24+
export type RateLimitInfo = {
25+
/**
26+
* The max number of requests one can make to that endpoint in the stipulated
27+
* window.
28+
*/
229
limit: number
30+
31+
/**
32+
* The number of requests already made to that endpoint.
33+
*/
334
used: number
35+
36+
/**
37+
* The number of requests that can be made before reaching the rate limit.
38+
*/
439
remaining: number
40+
41+
/**
42+
* The timestamp at which the window resets, and one's hit count is set to zero.
43+
*/
544
reset?: Date
6-
// Todo: policy
45+
46+
// TODO: policy
747
}
848

9-
export type RateLimitOptions = {
10-
reset?: 'date' | 'unix' | 'seconds' | 'milliseconds'
49+
/**
50+
* Options that configure how the library parses the headers.
51+
*/
52+
export type ParserOptions = {
53+
/**
54+
* How to parse the `reset` field. If unset, the parser will guess based on
55+
* the content of the header.
56+
*/
57+
reset:
58+
| 'date' // Pass the value to `new Date(...)` to let the JavaScript engine parse it.
59+
| 'unix' // Treat the value as the number of seconds since January 1, 1970 (A.K.A a UNIX epoch timestamp).
60+
| 'seconds' // Treat the value as the number of seconds from the current time.
61+
| 'milliseconds' // Treat the value as the number of milliseconds from the current time.
1162
}

0 commit comments

Comments
 (0)