aboutsummaryrefslogtreecommitdiffstats
path: root/vanilla/node_modules/undici/lib/cache
diff options
context:
space:
mode:
Diffstat (limited to 'vanilla/node_modules/undici/lib/cache')
-rw-r--r--vanilla/node_modules/undici/lib/cache/memory-cache-store.js234
-rw-r--r--vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js461
2 files changed, 0 insertions, 695 deletions
diff --git a/vanilla/node_modules/undici/lib/cache/memory-cache-store.js b/vanilla/node_modules/undici/lib/cache/memory-cache-store.js
deleted file mode 100644
index dba29ae..0000000
--- a/vanilla/node_modules/undici/lib/cache/memory-cache-store.js
+++ /dev/null
@@ -1,234 +0,0 @@
-'use strict'
-
-const { Writable } = require('node:stream')
-const { EventEmitter } = require('node:events')
-const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
-
-/**
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheKey} CacheKey
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheValue} CacheValue
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
- * @typedef {import('../../types/cache-interceptor.d.ts').default.GetResult} GetResult
- */
-
-/**
- * @implements {CacheStore}
- * @extends {EventEmitter}
- */
-class MemoryCacheStore extends EventEmitter {
- #maxCount = 1024
- #maxSize = 104857600 // 100MB
- #maxEntrySize = 5242880 // 5MB
-
- #size = 0
- #count = 0
- #entries = new Map()
- #hasEmittedMaxSizeEvent = false
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.MemoryCacheStoreOpts | undefined} [opts]
- */
- constructor (opts) {
- super()
- if (opts) {
- if (typeof opts !== 'object') {
- throw new TypeError('MemoryCacheStore options must be an object')
- }
-
- if (opts.maxCount !== undefined) {
- if (
- typeof opts.maxCount !== 'number' ||
- !Number.isInteger(opts.maxCount) ||
- opts.maxCount < 0
- ) {
- throw new TypeError('MemoryCacheStore options.maxCount must be a non-negative integer')
- }
- this.#maxCount = opts.maxCount
- }
-
- if (opts.maxSize !== undefined) {
- if (
- typeof opts.maxSize !== 'number' ||
- !Number.isInteger(opts.maxSize) ||
- opts.maxSize < 0
- ) {
- throw new TypeError('MemoryCacheStore options.maxSize must be a non-negative integer')
- }
- this.#maxSize = opts.maxSize
- }
-
- if (opts.maxEntrySize !== undefined) {
- if (
- typeof opts.maxEntrySize !== 'number' ||
- !Number.isInteger(opts.maxEntrySize) ||
- opts.maxEntrySize < 0
- ) {
- throw new TypeError('MemoryCacheStore options.maxEntrySize must be a non-negative integer')
- }
- this.#maxEntrySize = opts.maxEntrySize
- }
- }
- }
-
- /**
- * Get the current size of the cache in bytes
- * @returns {number} The current size of the cache in bytes
- */
- get size () {
- return this.#size
- }
-
- /**
- * Check if the cache is full (either max size or max count reached)
- * @returns {boolean} True if the cache is full, false otherwise
- */
- isFull () {
- return this.#size >= this.#maxSize || this.#count >= this.#maxCount
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} req
- * @returns {import('../../types/cache-interceptor.d.ts').default.GetResult | undefined}
- */
- get (key) {
- assertCacheKey(key)
-
- const topLevelKey = `${key.origin}:${key.path}`
-
- const now = Date.now()
- const entries = this.#entries.get(topLevelKey)
-
- const entry = entries ? findEntry(key, entries, now) : null
-
- return entry == null
- ? undefined
- : {
- statusMessage: entry.statusMessage,
- statusCode: entry.statusCode,
- headers: entry.headers,
- body: entry.body,
- vary: entry.vary ? entry.vary : undefined,
- etag: entry.etag,
- cacheControlDirectives: entry.cacheControlDirectives,
- cachedAt: entry.cachedAt,
- staleAt: entry.staleAt,
- deleteAt: entry.deleteAt
- }
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} val
- * @returns {Writable | undefined}
- */
- createWriteStream (key, val) {
- assertCacheKey(key)
- assertCacheValue(val)
-
- const topLevelKey = `${key.origin}:${key.path}`
-
- const store = this
- const entry = { ...key, ...val, body: [], size: 0 }
-
- return new Writable({
- write (chunk, encoding, callback) {
- if (typeof chunk === 'string') {
- chunk = Buffer.from(chunk, encoding)
- }
-
- entry.size += chunk.byteLength
-
- if (entry.size >= store.#maxEntrySize) {
- this.destroy()
- } else {
- entry.body.push(chunk)
- }
-
- callback(null)
- },
- final (callback) {
- let entries = store.#entries.get(topLevelKey)
- if (!entries) {
- entries = []
- store.#entries.set(topLevelKey, entries)
- }
- const previousEntry = findEntry(key, entries, Date.now())
- if (previousEntry) {
- const index = entries.indexOf(previousEntry)
- entries.splice(index, 1, entry)
- store.#size -= previousEntry.size
- } else {
- entries.push(entry)
- store.#count += 1
- }
-
- store.#size += entry.size
-
- // Check if cache is full and emit event if needed
- if (store.#size > store.#maxSize || store.#count > store.#maxCount) {
- // Emit maxSizeExceeded event if we haven't already
- if (!store.#hasEmittedMaxSizeEvent) {
- store.emit('maxSizeExceeded', {
- size: store.#size,
- maxSize: store.#maxSize,
- count: store.#count,
- maxCount: store.#maxCount
- })
- store.#hasEmittedMaxSizeEvent = true
- }
-
- // Perform eviction
- for (const [key, entries] of store.#entries) {
- for (const entry of entries.splice(0, entries.length / 2)) {
- store.#size -= entry.size
- store.#count -= 1
- }
- if (entries.length === 0) {
- store.#entries.delete(key)
- }
- }
-
- // Reset the event flag after eviction
- if (store.#size < store.#maxSize && store.#count < store.#maxCount) {
- store.#hasEmittedMaxSizeEvent = false
- }
- }
-
- callback(null)
- }
- })
- }
-
- /**
- * @param {CacheKey} key
- */
- delete (key) {
- if (typeof key !== 'object') {
- throw new TypeError(`expected key to be object, got ${typeof key}`)
- }
-
- const topLevelKey = `${key.origin}:${key.path}`
-
- for (const entry of this.#entries.get(topLevelKey) ?? []) {
- this.#size -= entry.size
- this.#count -= 1
- }
- this.#entries.delete(topLevelKey)
- }
-}
-
-function findEntry (key, entries, now) {
- return entries.find((entry) => (
- entry.deleteAt > now &&
- entry.method === key.method &&
- (entry.vary == null || Object.keys(entry.vary).every(headerName => {
- if (entry.vary[headerName] === null) {
- return key.headers[headerName] === undefined
- }
-
- return entry.vary[headerName] === key.headers[headerName]
- }))
- ))
-}
-
-module.exports = MemoryCacheStore
diff --git a/vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js b/vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js
deleted file mode 100644
index 7cb4aa7..0000000
--- a/vanilla/node_modules/undici/lib/cache/sqlite-cache-store.js
+++ /dev/null
@@ -1,461 +0,0 @@
-'use strict'
-
-const { Writable } = require('node:stream')
-const { assertCacheKey, assertCacheValue } = require('../util/cache.js')
-
-let DatabaseSync
-
-const VERSION = 3
-
-// 2gb
-const MAX_ENTRY_SIZE = 2 * 1000 * 1000 * 1000
-
-/**
- * @typedef {import('../../types/cache-interceptor.d.ts').default.CacheStore} CacheStore
- * @implements {CacheStore}
- *
- * @typedef {{
- * id: Readonly<number>,
- * body?: Uint8Array
- * statusCode: number
- * statusMessage: string
- * headers?: string
- * vary?: string
- * etag?: string
- * cacheControlDirectives?: string
- * cachedAt: number
- * staleAt: number
- * deleteAt: number
- * }} SqliteStoreValue
- */
-module.exports = class SqliteCacheStore {
- #maxEntrySize = MAX_ENTRY_SIZE
- #maxCount = Infinity
-
- /**
- * @type {import('node:sqlite').DatabaseSync}
- */
- #db
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #getValuesQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #updateValueQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #insertValueQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #deleteExpiredValuesQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #deleteByUrlQuery
-
- /**
- * @type {import('node:sqlite').StatementSync}
- */
- #countEntriesQuery
-
- /**
- * @type {import('node:sqlite').StatementSync | null}
- */
- #deleteOldValuesQuery
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.SqliteCacheStoreOpts | undefined} opts
- */
- constructor (opts) {
- if (opts) {
- if (typeof opts !== 'object') {
- throw new TypeError('SqliteCacheStore options must be an object')
- }
-
- if (opts.maxEntrySize !== undefined) {
- if (
- typeof opts.maxEntrySize !== 'number' ||
- !Number.isInteger(opts.maxEntrySize) ||
- opts.maxEntrySize < 0
- ) {
- throw new TypeError('SqliteCacheStore options.maxEntrySize must be a non-negative integer')
- }
-
- if (opts.maxEntrySize > MAX_ENTRY_SIZE) {
- throw new TypeError('SqliteCacheStore options.maxEntrySize must be less than 2gb')
- }
-
- this.#maxEntrySize = opts.maxEntrySize
- }
-
- if (opts.maxCount !== undefined) {
- if (
- typeof opts.maxCount !== 'number' ||
- !Number.isInteger(opts.maxCount) ||
- opts.maxCount < 0
- ) {
- throw new TypeError('SqliteCacheStore options.maxCount must be a non-negative integer')
- }
- this.#maxCount = opts.maxCount
- }
- }
-
- if (!DatabaseSync) {
- DatabaseSync = require('node:sqlite').DatabaseSync
- }
- this.#db = new DatabaseSync(opts?.location ?? ':memory:')
-
- this.#db.exec(`
- PRAGMA journal_mode = WAL;
- PRAGMA synchronous = NORMAL;
- PRAGMA temp_store = memory;
- PRAGMA optimize;
-
- CREATE TABLE IF NOT EXISTS cacheInterceptorV${VERSION} (
- -- Data specific to us
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- url TEXT NOT NULL,
- method TEXT NOT NULL,
-
- -- Data returned to the interceptor
- body BUF NULL,
- deleteAt INTEGER NOT NULL,
- statusCode INTEGER NOT NULL,
- statusMessage TEXT NOT NULL,
- headers TEXT NULL,
- cacheControlDirectives TEXT NULL,
- etag TEXT NULL,
- vary TEXT NULL,
- cachedAt INTEGER NOT NULL,
- staleAt INTEGER NOT NULL
- );
-
- CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_getValuesQuery ON cacheInterceptorV${VERSION}(url, method, deleteAt);
- CREATE INDEX IF NOT EXISTS idx_cacheInterceptorV${VERSION}_deleteByUrlQuery ON cacheInterceptorV${VERSION}(deleteAt);
- `)
-
- this.#getValuesQuery = this.#db.prepare(`
- SELECT
- id,
- body,
- deleteAt,
- statusCode,
- statusMessage,
- headers,
- etag,
- cacheControlDirectives,
- vary,
- cachedAt,
- staleAt
- FROM cacheInterceptorV${VERSION}
- WHERE
- url = ?
- AND method = ?
- ORDER BY
- deleteAt ASC
- `)
-
- this.#updateValueQuery = this.#db.prepare(`
- UPDATE cacheInterceptorV${VERSION} SET
- body = ?,
- deleteAt = ?,
- statusCode = ?,
- statusMessage = ?,
- headers = ?,
- etag = ?,
- cacheControlDirectives = ?,
- cachedAt = ?,
- staleAt = ?
- WHERE
- id = ?
- `)
-
- this.#insertValueQuery = this.#db.prepare(`
- INSERT INTO cacheInterceptorV${VERSION} (
- url,
- method,
- body,
- deleteAt,
- statusCode,
- statusMessage,
- headers,
- etag,
- cacheControlDirectives,
- vary,
- cachedAt,
- staleAt
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
- `)
-
- this.#deleteByUrlQuery = this.#db.prepare(
- `DELETE FROM cacheInterceptorV${VERSION} WHERE url = ?`
- )
-
- this.#countEntriesQuery = this.#db.prepare(
- `SELECT COUNT(*) AS total FROM cacheInterceptorV${VERSION}`
- )
-
- this.#deleteExpiredValuesQuery = this.#db.prepare(
- `DELETE FROM cacheInterceptorV${VERSION} WHERE deleteAt <= ?`
- )
-
- this.#deleteOldValuesQuery = this.#maxCount === Infinity
- ? null
- : this.#db.prepare(`
- DELETE FROM cacheInterceptorV${VERSION}
- WHERE id IN (
- SELECT
- id
- FROM cacheInterceptorV${VERSION}
- ORDER BY cachedAt DESC
- LIMIT ?
- )
- `)
- }
-
- close () {
- this.#db.close()
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @returns {(import('../../types/cache-interceptor.d.ts').default.GetResult & { body?: Buffer }) | undefined}
- */
- get (key) {
- assertCacheKey(key)
-
- const value = this.#findValue(key)
- return value
- ? {
- body: value.body ? Buffer.from(value.body.buffer, value.body.byteOffset, value.body.byteLength) : undefined,
- statusCode: value.statusCode,
- statusMessage: value.statusMessage,
- headers: value.headers ? JSON.parse(value.headers) : undefined,
- etag: value.etag ? value.etag : undefined,
- vary: value.vary ? JSON.parse(value.vary) : undefined,
- cacheControlDirectives: value.cacheControlDirectives
- ? JSON.parse(value.cacheControlDirectives)
- : undefined,
- cachedAt: value.cachedAt,
- staleAt: value.staleAt,
- deleteAt: value.deleteAt
- }
- : undefined
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue & { body: null | Buffer | Array<Buffer>}} value
- */
- set (key, value) {
- assertCacheKey(key)
-
- const url = this.#makeValueUrl(key)
- const body = Array.isArray(value.body) ? Buffer.concat(value.body) : value.body
- const size = body?.byteLength
-
- if (size && size > this.#maxEntrySize) {
- return
- }
-
- const existingValue = this.#findValue(key, true)
- if (existingValue) {
- // Updating an existing response, let's overwrite it
- this.#updateValueQuery.run(
- body,
- value.deleteAt,
- value.statusCode,
- value.statusMessage,
- value.headers ? JSON.stringify(value.headers) : null,
- value.etag ? value.etag : null,
- value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
- value.cachedAt,
- value.staleAt,
- existingValue.id
- )
- } else {
- this.#prune()
- // New response, let's insert it
- this.#insertValueQuery.run(
- url,
- key.method,
- body,
- value.deleteAt,
- value.statusCode,
- value.statusMessage,
- value.headers ? JSON.stringify(value.headers) : null,
- value.etag ? value.etag : null,
- value.cacheControlDirectives ? JSON.stringify(value.cacheControlDirectives) : null,
- value.vary ? JSON.stringify(value.vary) : null,
- value.cachedAt,
- value.staleAt
- )
- }
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheValue} value
- * @returns {Writable | undefined}
- */
- createWriteStream (key, value) {
- assertCacheKey(key)
- assertCacheValue(value)
-
- let size = 0
- /**
- * @type {Buffer[] | null}
- */
- const body = []
- const store = this
-
- return new Writable({
- decodeStrings: true,
- write (chunk, encoding, callback) {
- size += chunk.byteLength
-
- if (size < store.#maxEntrySize) {
- body.push(chunk)
- } else {
- this.destroy()
- }
-
- callback()
- },
- final (callback) {
- store.set(key, { ...value, body })
- callback()
- }
- })
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- */
- delete (key) {
- if (typeof key !== 'object') {
- throw new TypeError(`expected key to be object, got ${typeof key}`)
- }
-
- this.#deleteByUrlQuery.run(this.#makeValueUrl(key))
- }
-
- #prune () {
- if (Number.isFinite(this.#maxCount) && this.size <= this.#maxCount) {
- return 0
- }
-
- {
- const removed = this.#deleteExpiredValuesQuery.run(Date.now()).changes
- if (removed) {
- return removed
- }
- }
-
- {
- const removed = this.#deleteOldValuesQuery?.run(Math.max(Math.floor(this.#maxCount * 0.1), 1)).changes
- if (removed) {
- return removed
- }
- }
-
- return 0
- }
-
- /**
- * Counts the number of rows in the cache
- * @returns {Number}
- */
- get size () {
- const { total } = this.#countEntriesQuery.get()
- return total
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @returns {string}
- */
- #makeValueUrl (key) {
- return `${key.origin}/${key.path}`
- }
-
- /**
- * @param {import('../../types/cache-interceptor.d.ts').default.CacheKey} key
- * @param {boolean} [canBeExpired=false]
- * @returns {SqliteStoreValue | undefined}
- */
- #findValue (key, canBeExpired = false) {
- const url = this.#makeValueUrl(key)
- const { headers, method } = key
-
- /**
- * @type {SqliteStoreValue[]}
- */
- const values = this.#getValuesQuery.all(url, method)
-
- if (values.length === 0) {
- return undefined
- }
-
- const now = Date.now()
- for (const value of values) {
- if (now >= value.deleteAt && !canBeExpired) {
- return undefined
- }
-
- let matches = true
-
- if (value.vary) {
- const vary = JSON.parse(value.vary)
-
- for (const header in vary) {
- if (!headerValueEquals(headers[header], vary[header])) {
- matches = false
- break
- }
- }
- }
-
- if (matches) {
- return value
- }
- }
-
- return undefined
- }
-}
-
-/**
- * @param {string|string[]|null|undefined} lhs
- * @param {string|string[]|null|undefined} rhs
- * @returns {boolean}
- */
-function headerValueEquals (lhs, rhs) {
- if (lhs == null && rhs == null) {
- return true
- }
-
- if ((lhs == null && rhs != null) ||
- (lhs != null && rhs == null)) {
- return false
- }
-
- if (Array.isArray(lhs) && Array.isArray(rhs)) {
- if (lhs.length !== rhs.length) {
- return false
- }
-
- return lhs.every((x, i) => x === rhs[i])
- }
-
- return lhs === rhs
-}