UNPKG

@supabase-cache-helpers/postgrest-server

Version:

A collection of server-side caching utilities for working with Supabase.

1 lines 22.4 kB
{"version":3,"sources":["../src/index.ts","../src/stores/memory.ts","../src/stores/redis.ts","../src/context.ts","../src/key.ts","../src/utils.ts","../src/swr-cache.ts","../src/tiered-store.ts","../src/query-cache.ts"],"sourcesContent":["export * from './stores';\nexport * from './context';\nexport * from './query-cache';\n","import type { Entry } from './entry';\nimport type { Store } from './interface';\n\nexport type MemoryStoreConfig<TValue> = {\n persistentMap: Map<string, TValue>;\n // The maximum number of entries in the cache. If not set, the cache will grow indefinitely.\n capacity?: number;\n};\n\nexport class MemoryStore implements Store {\n private readonly state: Map<string, { expires: number; entry: Entry<any> }>;\n private readonly capacity?: number;\n\n public readonly name = 'memory';\n\n constructor(\n config: MemoryStoreConfig<{ expires: number; entry: Entry<any> }>,\n ) {\n this.state = config.persistentMap;\n this.capacity = config.capacity;\n }\n\n private setMostRecentlyUsed(\n key: string,\n value: { expires: number; entry: Entry<any> },\n ) {\n this.state.delete(key);\n this.state.set(key, value);\n }\n\n public async get<Result>(key: string): Promise<Entry<Result> | undefined> {\n const value = this.state.get(key);\n if (!value) {\n return Promise.resolve(undefined);\n }\n if (value.expires <= Date.now()) {\n await this.remove(key);\n }\n\n if (this.capacity) {\n this.setMostRecentlyUsed(key, value);\n }\n\n return Promise.resolve(value.entry);\n }\n\n public async set<Result>(key: string, entry: Entry<Result>): Promise<void> {\n if (this.capacity) {\n this.setMostRecentlyUsed(key, {\n expires: entry.staleUntil,\n entry,\n });\n } else {\n this.state.set(key, {\n expires: entry.staleUntil,\n entry,\n });\n }\n\n if (this.capacity && this.state.size > this.capacity) {\n const oldestKey = this.state.keys().next().value;\n if (oldestKey !== undefined) {\n this.state.delete(oldestKey);\n }\n }\n\n return Promise.resolve();\n }\n\n public async remove(keys: string | string[]): Promise<void> {\n const cacheKeys = Array.isArray(keys) ? keys : [keys];\n\n for (const key of cacheKeys) {\n this.state.delete(key);\n }\n return Promise.resolve();\n }\n\n public async removeByPrefix(prefix: string): Promise<void> {\n for (const key of this.state.keys()) {\n if (key.startsWith(prefix)) {\n this.state.delete(key);\n }\n }\n }\n}\n","import type { Entry } from './entry';\nimport type { Store } from './interface';\nimport type { Redis } from 'ioredis';\n\nexport type RedisStoreConfig = {\n redis: Redis;\n prefix?: string;\n};\n\nexport class RedisStore implements Store {\n private readonly redis: Redis;\n public readonly name = 'redis';\n private readonly prefix: string;\n\n constructor(config: RedisStoreConfig) {\n this.redis = config.redis;\n this.prefix = config.prefix || 'sbch';\n }\n\n private buildCacheKey(key: string): string {\n return [this.prefix, key].join('::');\n }\n\n public async get<Result>(key: string): Promise<Entry<Result> | undefined> {\n const res = await this.redis.get(this.buildCacheKey(key));\n if (!res) return;\n\n return JSON.parse(res) as Entry<Result>;\n }\n\n public async set<Result>(key: string, entry: Entry<Result>): Promise<void> {\n await this.redis.set(\n this.buildCacheKey(key),\n JSON.stringify(entry),\n 'PXAT',\n entry.staleUntil,\n );\n }\n\n public async remove(keys: string | string[]): Promise<void> {\n const cacheKeys = (Array.isArray(keys) ? keys : [keys]).map((key) =>\n this.buildCacheKey(key).toString(),\n );\n this.redis.del(...cacheKeys);\n }\n\n public async removeByPrefix(prefix: string): Promise<void> {\n const pattern = `${prefix}*`;\n let cursor = '0';\n\n do {\n const [nextCursor, keys] = await this.redis.scan(\n cursor,\n 'MATCH',\n pattern,\n 'COUNT',\n 100,\n );\n cursor = nextCursor;\n\n if (keys.length > 0) {\n await this.redis.del(...keys);\n }\n } while (cursor !== '0');\n }\n}\n","export interface Context {\n waitUntil: (p: Promise<unknown>) => void;\n}\n\nexport class DefaultStatefulContext implements Context {\n public waitUntil<TPromise = unknown>(_p: Promise<TPromise>) {\n // do nothing, the promise will resolve on its own\n }\n}\n","import {\n AnyPostgrestResponse,\n PostgrestParser,\n isPostgrestBuilder,\n} from '@supabase-cache-helpers/postgrest-core';\n\nconst SEPARATOR = '$';\n\nexport function encode<Result>(\n query: PromiseLike<AnyPostgrestResponse<Result>>,\n) {\n if (!isPostgrestBuilder<Result>(query)) {\n throw new Error('Query is not a PostgrestBuilder');\n }\n\n const parser = new PostgrestParser<Result>(query);\n return [\n parser.schema,\n parser.table,\n parser.queryKey,\n parser.bodyKey ?? 'null',\n `count=${parser.count}`,\n `head=${parser.isHead}`,\n parser.orderByKey,\n ].join(SEPARATOR);\n}\n\nexport function buildTablePrefix(schema: string, table: string) {\n return [schema, table].join(SEPARATOR);\n}\n","import { Value } from './stores/entry';\n\n/**\n * A result is empty if\n * - it does not contain a truhty data field\n * - it does not contain a count field\n * - data is an empty array\n *\n * @template Result - The Result of the query\n * @param result - The value to check\n * @returns true if the result is empty\n */\nexport function isEmpty<Result>(result: Value<Result>) {\n if (typeof result.count === 'number') {\n return false;\n }\n\n if (result.data === null || typeof result.data === 'undefined') {\n return true;\n }\n\n if (Array.isArray(result.data)) {\n return result.data.length === 0;\n }\n\n return false;\n}\n","import type { Context } from './context';\nimport { Value } from './stores/entry';\nimport { Store } from './stores/interface';\nimport { isEmpty } from './utils';\n\nexport type SwrCacheOpts = {\n ctx: Context;\n store: Store;\n fresh: number;\n stale: number;\n};\n\n/**\n * Internal cache implementation for an individual namespace\n */\nexport class SwrCache {\n private readonly ctx: Context;\n private readonly store: Store;\n private readonly fresh: number;\n private readonly stale: number;\n\n constructor({ ctx, store, fresh, stale }: SwrCacheOpts) {\n this.ctx = ctx;\n this.store = store;\n this.fresh = fresh;\n this.stale = stale;\n }\n\n /**\n * Invalidate all keys that start with the given prefix\n **/\n async removeByPrefix(prefix: string) {\n return this.store.removeByPrefix(prefix);\n }\n\n /**\n * Return the cached value\n *\n * The response will be `undefined` for cache misses or `null` when the key was not found in the origin\n */\n public async get<Result>(key: string): Promise<Value<Result> | undefined> {\n const res = await this._get<Result>(key);\n return res.value;\n }\n\n private async _get<Result>(\n key: string,\n ): Promise<{ value: Value<Result> | undefined; revalidate?: boolean }> {\n const res = await this.store.get<Result>(key);\n\n const now = Date.now();\n if (!res) {\n return { value: undefined };\n }\n\n if (now >= res.staleUntil) {\n this.ctx.waitUntil(this.remove(key));\n return { value: undefined };\n }\n if (now >= res.freshUntil) {\n return { value: res.value, revalidate: true };\n }\n\n return { value: res.value };\n }\n\n /**\n * Set the value\n */\n public async set<Result>(\n key: string,\n value: Value<Result>,\n opts?: {\n fresh?: number;\n stale?: number;\n },\n ): Promise<void> {\n const now = Date.now();\n return this.store.set(key, {\n value,\n freshUntil: now + (opts?.fresh ?? this.fresh),\n staleUntil: now + (opts?.stale ?? this.stale),\n });\n }\n\n /**\n * Removes the key from the cache.\n */\n public async remove(key: string): Promise<void> {\n return this.store.remove(key);\n }\n\n public async swr<Result>(\n key: string,\n loadFromOrigin: (key: string) => Promise<Value<Result>>,\n opts?: {\n fresh: number;\n stale: number;\n },\n ): Promise<Value<Result>> {\n const res = await this._get<Result>(key);\n\n const { value, revalidate } = res;\n\n if (typeof value !== 'undefined') {\n if (revalidate) {\n this.ctx.waitUntil(\n loadFromOrigin(key).then((res) => {\n if (!isEmpty(res)) {\n this.set(key, res, opts);\n }\n }),\n );\n }\n\n return value;\n }\n\n const loadedValue = await loadFromOrigin(key);\n if (!isEmpty(loadedValue)) {\n this.ctx.waitUntil(this.set(key, loadedValue));\n }\n return loadedValue;\n }\n}\n","import type { Context } from './context';\nimport { Entry } from './stores/entry';\nimport { Store } from './stores/interface';\n\n/**\n * TieredCache is a cache that will first check the memory cache, then the zone cache.\n */\nexport class TieredStore implements Store {\n private ctx: Context;\n private readonly tiers: Store[];\n public readonly name = 'tiered';\n\n /**\n * Create a new tiered store\n * Stored are checked in the order they are provided\n * The first store to return a value will be used to populate all previous stores\n *\n *\n * `stores` can accept `undefined` as members to allow you to construct the tiers dynamically\n * @example\n * ```ts\n * new TieredStore(ctx, [\n * new MemoryStore(..),\n * process.env.ENABLE_X_STORE ? new XStore(..) : undefined\n * ])\n * ```\n */\n constructor(ctx: Context, stores: (Store | undefined)[]) {\n this.ctx = ctx;\n this.tiers = stores.filter(Boolean) as Store[];\n }\n\n /**\n * Return the cached value\n *\n * The response will be `undefined` for cache misses or `null` when the key was not found in the origin\n */\n public async get<Result>(key: string): Promise<Entry<Result> | undefined> {\n if (this.tiers.length === 0) {\n return;\n }\n\n for (let i = 0; i < this.tiers.length; i++) {\n const res = await this.tiers[i].get<Result>(key);\n\n if (!res) {\n return;\n }\n\n // Fill all lower caches\n this.ctx.waitUntil(\n Promise.all(\n this.tiers.filter((_, j) => j < i).map((t) => () => t.set(key, res)),\n ),\n );\n\n return res;\n }\n }\n\n /**\n * Sets the value for the given key.\n */\n public async set<Result>(key: string, value: Entry<Result>): Promise<void> {\n await Promise.all(this.tiers.map((t) => t.set(key, value)));\n }\n\n /**\n * Removes the key from the cache.\n */\n public async remove(key: string): Promise<void> {\n await Promise.all(this.tiers.map((t) => t.remove(key)));\n }\n\n /**\n * Removes all keys with the given prefix.\n */\n public async removeByPrefix(prefix: string): Promise<void> {\n await Promise.all(this.tiers.map((t) => t.removeByPrefix(prefix)));\n }\n}\n","import { Context } from './context';\nimport { buildTablePrefix, encode } from './key';\nimport { Value } from './stores/entry';\nimport { Store } from './stores/interface';\nimport { SwrCache } from './swr-cache';\nimport { TieredStore } from './tiered-store';\nimport { isEmpty } from './utils';\nimport { type AnyPostgrestResponse } from '@supabase-cache-helpers/postgrest-core';\nimport type {\n PostgrestMaybeSingleResponse,\n PostgrestResponse,\n PostgrestSingleResponse,\n} from '@supabase/postgrest-js';\n\nexport type QueryCacheOpts = {\n stores: Store[];\n fresh: number;\n stale: number;\n};\n\nexport type OperationOpts = Pick<QueryCacheOpts, 'fresh' | 'stale'>;\n\nexport class QueryCache {\n private readonly inner: SwrCache;\n\n /**\n * To prevent concurrent requests of the same data, all queries are deduplicated using\n * this map.\n */\n private readonly runningQueries: Map<\n string,\n PromiseLike<AnyPostgrestResponse<any>>\n > = new Map();\n\n constructor(ctx: Context, opts: QueryCacheOpts) {\n const tieredStore = new TieredStore(ctx, opts.stores);\n\n this.inner = new SwrCache({\n ctx,\n store: tieredStore,\n fresh: opts.fresh,\n stale: opts.stale,\n });\n }\n\n /**\n * Invalidate all cache entries for a given table\n */\n async invalidateQueries({\n schema,\n table,\n }: {\n schema: string;\n table: string;\n }) {\n const prefix = buildTablePrefix(schema, table);\n return this.inner.removeByPrefix(prefix);\n }\n\n /**\n * Perform a cached postgrest query\n */\n query<Result>(\n query: PromiseLike<PostgrestSingleResponse<Result>>,\n opts?: Partial<OperationOpts> & {\n store?: (result: PostgrestSingleResponse<Result>) => boolean;\n },\n ): Promise<PostgrestSingleResponse<Result>>;\n /**\n * Perform a cached postgrest query\n */\n query<Result>(\n query: PromiseLike<PostgrestMaybeSingleResponse<Result>>,\n opts?: Partial<OperationOpts> & {\n store?: (result: PostgrestMaybeSingleResponse<Result>) => boolean;\n },\n ): Promise<PostgrestMaybeSingleResponse<Result>>;\n /**\n * Perform a cached postgrest query\n */\n query<Result>(\n query: PromiseLike<PostgrestResponse<Result>>,\n opts?: Partial<OperationOpts> & {\n store?: (result: PostgrestResponse<Result>) => boolean;\n },\n ): Promise<PostgrestResponse<Result>>;\n async query<Result>(\n query: PromiseLike<AnyPostgrestResponse<Result>>,\n opts?: Partial<OperationOpts> & {\n store?: (result: AnyPostgrestResponse<Result>) => boolean;\n },\n ): Promise<AnyPostgrestResponse<Result>> {\n const key = encode(query);\n\n const value = await this.inner.get<Result>(key);\n\n if (value) return value;\n\n const result = await this.dedupeQuery(query);\n\n if (!isEmpty(result) && (!opts?.store || opts.store(result))) {\n await this.inner.set(key, result, opts);\n }\n\n return result;\n }\n\n /**\n * Perform a cached postgrest query\n */\n swr<Result>(\n query: PromiseLike<PostgrestSingleResponse<Result>>,\n opts?: OperationOpts,\n ): Promise<PostgrestSingleResponse<Result>>;\n /**\n * Perform a cached postgrest query\n */\n swr<Result>(\n query: PromiseLike<PostgrestMaybeSingleResponse<Result>>,\n opts?: OperationOpts,\n ): Promise<PostgrestMaybeSingleResponse<Result>>;\n /**\n * Perform a cached postgrest query\n */\n swr<Result>(\n query: PromiseLike<PostgrestResponse<Result>>,\n opts?: OperationOpts,\n ): Promise<PostgrestResponse<Result>>;\n async swr<Result>(\n query: PromiseLike<AnyPostgrestResponse<Result>>,\n opts?: OperationOpts,\n ): Promise<AnyPostgrestResponse<Result>> {\n return await this.inner.swr(\n encode(query),\n () => this.dedupeQuery(query),\n opts,\n );\n }\n\n /**\n * Deduplicating the origin load helps when the same value is requested many times at once and is\n * not yet in the cache. If we don't deduplicate, we'd create a lot of unnecessary load on the db.\n */\n private async dedupeQuery<Result>(\n query: PromiseLike<AnyPostgrestResponse<Result>>,\n ): Promise<Value<Result>> {\n const key = encode(query);\n try {\n const querying = this.runningQueries.get(key);\n if (querying) {\n return querying;\n }\n\n this.runningQueries.set(key, query);\n return await query;\n } finally {\n this.runningQueries.delete(key);\n }\n }\n}\n"],"mappings":"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","names":["res"]}