Skip to content

Commit 977c314

Browse files
committed
fixup
1 parent e46c54c commit 977c314

File tree

1 file changed

+51
-99
lines changed

1 file changed

+51
-99
lines changed

lib/cache/memory-cache-store.js

+51-99
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@
33
const { Writable } = require('node:stream')
44

55
/**
6+
* @typedef {import('../../types/cache-interceptor.d.ts').default.CacheKey} CacheKey
67
* @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
8+
* @typedef {import('../../types/cache-interceptor.d.ts').default.CachedResponse} CachedResponse
9+
* @typedef {import('../../types/cache-interceptor.d.ts').default.GetResult} GetResult
710
* @implements {CacheStore}
8-
*
9-
* @typedef {{
10-
* locked: boolean
11-
* opts: import('../../types/cache-interceptor.d.ts').default.CachedResponse
12-
* body?: Buffer[]
13-
* }} MemoryStoreValue
1411
*/
1512
class MemoryCacheStore {
1613
#maxCount = Infinity
@@ -20,7 +17,7 @@ class MemoryCacheStore {
2017
#entryCount = 0
2118

2219
/**
23-
* @type {Map<string, Map<string, MemoryStoreValue[]>>}
20+
* @type {Map<string, Map<string, GetResult[]>>}
2421
*/
2522
#data = new Map()
2623

@@ -66,22 +63,8 @@ class MemoryCacheStore {
6663
* @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
6764
*/
6865
get (key) {
69-
if (typeof key !== 'object') {
70-
throw new TypeError(`expected key to be object, got ${typeof key}`)
71-
}
72-
73-
const values = this.#getValuesForRequest(key, false)
74-
if (!values) {
75-
return undefined
76-
}
77-
78-
const value = this.#findValue(key, values)
79-
80-
if (!value || value.locked) {
81-
return undefined
82-
}
83-
84-
return { ...value.opts, body: value.body }
66+
const values = this.#getValuesForRequest(key)
67+
return findValue(key, values)
8568
}
8669

8770
/**
@@ -98,86 +81,52 @@ class MemoryCacheStore {
9881
}
9982

10083
if (this.isFull) {
101-
return undefined
84+
this.#prune()
10285
}
10386

104-
const values = this.#getValuesForRequest(key, true)
105-
106-
let value = this.#findValue(key, values)
107-
if (!value) {
108-
// The value doesn't already exist, meaning we haven't cached this
109-
// response before. Let's assign it a value and insert it into our data
110-
// property.
87+
if (this.isFull) {
88+
return undefined
89+
}
11190

112-
if (this.isFull) {
113-
// Or not, we don't have space to add another response
114-
return undefined
115-
}
91+
const values = this.#getValuesForRequest(key)
11692

117-
if (this.#entryCount++ > this.#maxEntries) {
118-
this.#prune()
119-
}
93+
let value = findValue(key, values)
12094

121-
value = { locked: true, opts }
95+
if (!value) {
96+
value = { ...opts, body: null }
12297
values.push(value)
123-
} else {
124-
// Check if there's already another request writing to the value or
125-
// a request reading from it
126-
if (value.locked) {
127-
return undefined
128-
}
129-
130-
// Empty it so we can overwrite it
131-
value.body = []
13298
}
13399

134100
let currentSize = 0
135-
/**
136-
* @type {Buffer[] | null}
137-
*/
138-
let body = []
101+
102+
const body = []
139103
const maxEntrySize = this.#maxEntrySize
140104

141-
const writable = new Writable({
105+
return new Writable({
142106
write (chunk, encoding, callback) {
143-
if (key.method === 'HEAD') {
144-
throw new Error('HEAD request shouldn\'t have a body')
145-
}
146-
147-
if (!body) {
148-
return callback()
149-
}
150-
151107
if (typeof chunk === 'string') {
152108
chunk = Buffer.from(chunk, encoding)
153109
}
154110

155111
currentSize += chunk.byteLength
156112

157113
if (currentSize >= maxEntrySize) {
158-
body = null
159-
this.end()
160-
return callback()
114+
this.destroy()
115+
} else {
116+
body.push(chunk)
161117
}
162118

163-
body.push(chunk)
164119
callback()
165120
},
166121
final (callback) {
167-
value.locked = false
168-
if (body !== null) {
169-
value.body = body
170-
}
171-
122+
Object.assign(value, opts, { body })
172123
callback()
173124
}
174125
})
175-
176-
return writable
177126
}
178127

179128
/**
180-
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
129+
* @param {CacheKey} key
181130
*/
182131
delete (key) {
183132
this.#data.delete(`${key.origin}:${key.path}`)
@@ -186,53 +135,36 @@ class MemoryCacheStore {
186135
/**
187136
* Gets all of the requests of the same origin, path, and method. Does not
188137
* take the `vary` property into account.
189-
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
190-
* @param {boolean} [makeIfDoesntExist=false]
191-
* @returns {MemoryStoreValue[] | undefined}
138+
* @param {CacheKey} key
139+
* @returns {GetResult[]}
192140
*/
193-
#getValuesForRequest (key, makeIfDoesntExist) {
141+
#getValuesForRequest (key) {
142+
if (typeof key !== 'object') {
143+
throw new TypeError(`expected key to be object, got ${typeof key}`)
144+
}
145+
194146
// https://www.rfc-editor.org/rfc/rfc9111.html#section-2-3
195147
const topLevelKey = `${key.origin}:${key.path}`
196148
let cachedPaths = this.#data.get(topLevelKey)
197149
if (!cachedPaths) {
198-
if (!makeIfDoesntExist) {
199-
return undefined
200-
}
201-
202150
cachedPaths = new Map()
203151
this.#data.set(topLevelKey, cachedPaths)
204152
}
205153

206154
let value = cachedPaths.get(key.method)
207-
if (!value && makeIfDoesntExist) {
155+
if (!value) {
208156
value = []
209157
cachedPaths.set(key.method, value)
210158
}
211159

212160
return value
213161
}
214162

215-
/**
216-
* Given a list of values of a certain request, this decides the best value
217-
* to respond with.
218-
* @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req
219-
* @param {MemoryStoreValue[]} values
220-
* @returns {(MemoryStoreValue) | undefined}
221-
*/
222-
#findValue (req, values) {
223-
const now = Date.now()
224-
return values.find(({ opts: { deleteAt, vary }, body }) => (
225-
body != null &&
226-
deleteAt > now &&
227-
(!vary || Object.keys(vary).every(key => vary[key] === req.headers?.[key]))
228-
))
229-
}
230-
231163
#prune () {
232164
const now = Date.now()
233165
for (const [key, cachedPaths] of this.#data) {
234166
for (const [method, prev] of cachedPaths) {
235-
const next = prev.filter(({ opts, body }) => body == null || opts.deleteAt > now)
167+
const next = prev.filter(({ deleteAt }) => deleteAt > now)
236168
if (next.length === 0) {
237169
cachedPaths.delete(method)
238170
if (cachedPaths.size === 0) {
@@ -247,4 +179,24 @@ class MemoryCacheStore {
247179
}
248180
}
249181

182+
/**
183+
* Given a list of values of a certain request, this decides the best value
184+
* to respond with.
185+
* @param {CacheKey} key
186+
* @param {GetResult[] | undefined } values
187+
* @returns {(GetResult) | undefined}
188+
*/
189+
function findValue (key, values) {
190+
if (typeof key !== 'object') {
191+
throw new TypeError(`expected key to be object, got ${typeof key}`)
192+
}
193+
194+
const now = Date.now()
195+
return values?.find(({ deleteAt, vary, body }) => (
196+
body != null &&
197+
deleteAt > now &&
198+
(!vary || Object.keys(vary).every(headerName => vary[headerName] === key.headers?.[headerName]))
199+
))
200+
}
201+
250202
module.exports = MemoryCacheStore

0 commit comments

Comments
 (0)