@supabase-cache-helpers/postgrest-server
Version:
A collection of server-side caching utilities for working with Supabase.
171 lines (170 loc) • 4.01 kB
JavaScript
// src/stores/memory.ts
var MemoryStore = class {
state;
capacity;
name = "memory";
constructor(config) {
this.state = config.persistentMap;
this.capacity = config.capacity;
}
setMostRecentlyUsed(key, value) {
this.state.delete(key);
this.state.set(key, value);
}
async get(key) {
const value = this.state.get(key);
if (!value) {
return Promise.resolve(void 0);
}
if (value.expires <= Date.now()) {
await this.remove(key);
}
if (this.capacity) {
this.setMostRecentlyUsed(key, value);
}
return Promise.resolve(value.entry);
}
async set(key, entry) {
if (this.capacity) {
this.setMostRecentlyUsed(key, {
expires: entry.staleUntil,
entry
});
} else {
this.state.set(key, {
expires: entry.staleUntil,
entry
});
}
if (this.capacity && this.state.size > this.capacity) {
const oldestKey = this.state.keys().next().value;
if (oldestKey !== void 0) {
this.state.delete(oldestKey);
}
}
return Promise.resolve();
}
async remove(keys) {
const cacheKeys = Array.isArray(keys) ? keys : [keys];
for (const key of cacheKeys) {
this.state.delete(key);
}
return Promise.resolve();
}
async removeByPrefix(prefix) {
for (const key of this.state.keys()) {
if (key.startsWith(prefix)) {
this.state.delete(key);
}
}
}
async removeByPattern(pattern) {
const regex = this.globToRegex(pattern);
for (const key of this.state.keys()) {
if (regex.test(key)) {
this.state.delete(key);
}
}
}
/**
* Convert a glob pattern to a regex, handling escaped characters.
* Supports: * (any chars), ? (single char), \* \? \[ \] (literals)
*/
globToRegex(pattern) {
let regex = "^";
let i = 0;
while (i < pattern.length) {
const char = pattern[i];
if (char === "\\" && i + 1 < pattern.length) {
const next = pattern[i + 1];
regex += "\\" + next;
i += 2;
} else if (char === "*") {
regex += ".*";
i++;
} else if (char === "?") {
regex += ".";
i++;
} else if (/[.+^${}()|[\]\\]/.test(char)) {
regex += "\\" + char;
i++;
} else {
regex += char;
i++;
}
}
regex += "$";
return new RegExp(regex);
}
};
// src/stores/redis.ts
var RedisStore = class {
redis;
name = "redis";
prefix;
constructor(config) {
this.redis = config.redis;
this.prefix = config.prefix || "sbch";
}
buildCacheKey(key) {
return [this.prefix, key].join("::");
}
async get(key) {
const res = await this.redis.get(this.buildCacheKey(key));
if (!res) return;
return JSON.parse(res);
}
async set(key, entry) {
await this.redis.set(
this.buildCacheKey(key),
JSON.stringify(entry),
"PXAT",
entry.staleUntil
);
}
async remove(keys) {
const cacheKeys = (Array.isArray(keys) ? keys : [keys]).map(
(key) => this.buildCacheKey(key).toString()
);
this.redis.del(...cacheKeys);
}
async removeByPrefix(prefix) {
const pattern = `${prefix}*`;
let cursor = "0";
do {
const [nextCursor, keys] = await this.redis.scan(
cursor,
"MATCH",
pattern,
"COUNT",
100
);
cursor = nextCursor;
if (keys.length > 0) {
await this.redis.del(...keys);
}
} while (cursor !== "0");
}
async removeByPattern(pattern) {
const fullPattern = this.buildCacheKey(pattern);
let cursor = "0";
do {
const [nextCursor, keys] = await this.redis.scan(
cursor,
"MATCH",
fullPattern,
"COUNT",
100
);
cursor = nextCursor;
if (keys.length > 0) {
await this.redis.del(...keys);
}
} while (cursor !== "0");
}
};
export {
MemoryStore,
RedisStore
};
//# sourceMappingURL=stores.js.map