From e9d3563f3c881a5c9fee9bc5373846bce950fca2 Mon Sep 17 00:00:00 2001 From: "Juan D. Jara" Date: Mon, 16 Oct 2023 14:10:42 +0200 Subject: [PATCH] repalce upstash SDK wtih withRedis util and ioredis --- app/lib/cache.server.ts | 32 ++++-- app/lib/projects.server.ts | 105 ++++++++++++------- app/lib/redis.server.ts | 22 ++++ package-lock.json | 207 +++++++++++++++++++++++++++---------- package.json | 2 +- 5 files changed, 261 insertions(+), 107 deletions(-) create mode 100644 app/lib/redis.server.ts diff --git a/app/lib/cache.server.ts b/app/lib/cache.server.ts index 1d8a161..c28b876 100644 --- a/app/lib/cache.server.ts +++ b/app/lib/cache.server.ts @@ -1,32 +1,42 @@ -import { Redis } from "@upstash/redis" import type { ParsedFile, TreeItem } from "./github" - -const db = Redis.fromEnv() +import { withRedis } from "./redis.server" export async function getTreeCache(repo: string, sha: string) { - return await db.get(`tree:${repo}:${sha}`) as TreeItem[] + return withRedis(async (db) => { + const data = await db.get(`tree:${repo}:${sha}`) + return data && JSON.parse(data) + }) } export async function setTreeCache(repo: string, sha: string, tree: TreeItem[]) { - await db.set(`tree:${repo}:${sha}`, tree, { - ex: 60 * 60 * 24 // 1 day + return withRedis(async (db) => { + const oneDay = 60 * 60 * 24 + await db.setex(`tree:${repo}:${sha}`, oneDay, JSON.stringify(tree)) }) } export async function deleteTreeCache(repo: string, sha: string) { - await db.del(`tree:${repo}:${sha}`) + return withRedis(async (db) => { + await db.del(`tree:${repo}:${sha}`) + }) } export async function getFileCache(repo: string, branch: string, path: string) { - return await db.get(`file:${repo}/${branch}/${path}`) as ParsedFile + return withRedis(async (db) => { + const data = await db.get(`file:${repo}/${branch}/${path}`) + return data && JSON.parse(data) + }) } export async function setFileCache(repo: string, branch: string, path: string, file: ParsedFile) { - await db.set(`file:${repo}/${branch}/${path}`, file, { - ex: 60 * 60 * 24 // 1 day + return withRedis(async (db) => { + const oneDay = 60 * 60 * 24 + await db.setex(`file:${repo}/${branch}/${path}`, oneDay, JSON.stringify(file)) }) } export async function deleteFileCache(repo: string, branch: string, path: string) { - await db.del(`file:${repo}/${branch}/${path}`) + return withRedis(async (db) => { + await db.del(`file:${repo}/${branch}/${path}`) + }) } diff --git a/app/lib/projects.server.ts b/app/lib/projects.server.ts index d9c2a2a..e920f4d 100644 --- a/app/lib/projects.server.ts +++ b/app/lib/projects.server.ts @@ -1,11 +1,9 @@ -import { Redis } from "@upstash/redis" import type { ParsedFile } from "./github" import { FileMode, commitAndPush, deleteFile, getFileContent, getRepoFiles, saveFile } from "./github" import { getBasename, getDirname, isMarkdown } from "./pathUtils" import matter from 'front-matter' import { deleteFileCache } from "./cache.server" - -const db = Redis.fromEnv() +import { withRedis } from "./redis.server" export type Project = { id: number @@ -44,44 +42,61 @@ export type ProjectConfig = { const NEXT_PROJECT_KEY = 'next_project_id' export async function getUserProjects(user: string) { - const ids = await db.smembers(`projects:${user}`) - if (ids.length === 0) { - return [] - } - - const projects = await db.mget(...ids.map(id => `project:${id}`)) as Project[] - return projects.sort((a, b) => a.title.localeCompare(b.title)) + return withRedis(async (db) => { + const ids = await db.smembers(`projects:${user}`) + if (ids.length === 0) { + return [] + } + + const projects = await db.mget(...ids.map(id => `project:${id}`)) + return projects + .filter((p) => p) + .map((p) => JSON.parse(p!) as Project) + .sort((a, b) => a.title.localeCompare(b.title)) + }) } export async function getProject(id: number) { - return await db.get(`project:${id}`) as Project + return withRedis(async (db) => { + const data = await db.get(`project:${id}`) + return data && JSON.parse(data) + }) } export async function getIdForRepo(repo: string) { - return await db.get(`repo:${repo}`) + return withRedis(async (db) => { + const data = await db.get(`repo:${repo}`) + return data ? Number(data) : null + }) } export async function createProject(project: Omit) { - const id = await db.incr(NEXT_PROJECT_KEY) - await Promise.all([ - db.sadd(`projects:${project.user}`, id), - db.set(`project:${id}`, { ...project, id }), - db.set(`repo:${project.repo}`, id) - ]) - - return id + return withRedis(async (db) => { + const id = await db.incr(NEXT_PROJECT_KEY) + await Promise.all([ + db.sadd(`projects:${project.user}`, id), + db.set(`project:${id}`, JSON.stringify({ ...project, id })), + db.set(`repo:${project.repo}`, id) + ]) + + return id + }) } export async function updateProject(project: Project) { - return db.set(`project:${project.id}`, project) + return withRedis(async (db) => { + await db.set(`project:${project.id}`, JSON.stringify(project)) + }) } export async function deleteProject(project: Project) { - return Promise.all([ - db.srem(`projects:${project.user}`, project.id), - db.del(`project:${project.id}`), - db.del(`repo:${project.repo}`) - ]) + return withRedis(async (db) => { + await Promise.all([ + db.srem(`projects:${project.user}`, project.id), + db.del(`project:${project.id}`), + db.del(`repo:${project.repo}`) + ]) + }) } export const CONFIG_FILE_NAME = 'pressunto.config.json' @@ -250,27 +265,37 @@ type SaveDraftParams = { } export async function saveDraft(params: SaveDraftParams) { - const { project, file } = params - const key = `draft:${project.id}:${encodeURIComponent(file.path)}` - await db.set(key, file) - return key + return withRedis(async (db) => { + const { project, file } = params + const key = `draft:${project.id}:${encodeURIComponent(file.path)}` + await db.set(key, JSON.stringify(file)) + return key + }) } export async function getDraft(projectId: number, path: string) { - const draft = await db.get(`draft:${projectId}:${encodeURIComponent(path)}`) - return draft + return withRedis(async (db) => { + const data = await db.get(`draft:${projectId}:${encodeURIComponent(path)}`) + return data && JSON.parse(data) + }) } export async function deleteDraft(projectId: number, path: string) { - await db.del(`draft:${projectId}:${encodeURIComponent(path)}`) + return withRedis(async (db) => { + await db.del(`draft:${projectId}:${encodeURIComponent(path)}`) + }) } export async function renameDraft(projectId: number, oldPath: string, newPath: string) { - const draft = await getDraft(projectId, oldPath) - if (!draft) { - return - } - - await db.set(`draft:${projectId}:${encodeURIComponent(newPath)}`, draft) - await db.del(`draft:${projectId}:${encodeURIComponent(oldPath)}`) + return withRedis(async (db) => { + const draft = await getDraft(projectId, oldPath) + if (!draft) { + return + } + + await Promise.all([ + db.set(`draft:${projectId}:${encodeURIComponent(newPath)}`, JSON.stringify(draft)), + db.del(`draft:${projectId}:${encodeURIComponent(oldPath)}`), + ]) + }) } diff --git a/app/lib/redis.server.ts b/app/lib/redis.server.ts new file mode 100644 index 0000000..88e6e57 --- /dev/null +++ b/app/lib/redis.server.ts @@ -0,0 +1,22 @@ +import { Redis } from 'ioredis' + +const REDIS_URL = process.env.REDIS_URL + +/** + * Create a redis instance only for the duration of the function call + */ +export async function withRedis(fn: (redis: Redis) => Promise) { + if (!REDIS_URL) { + throw new Error('REDIS_URL not found in process.env') + } + const redis = new Redis( + REDIS_URL, + { family: 6, reconnectOnError: () => 1 } + ) + redis.on('error', err => { + console.error('Redis error: ', err) + }) + const result = await fn(redis) + await redis.quit() + return result +} diff --git a/package-lock.json b/package-lock.json index 9aa1844..57f95ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,12 +23,12 @@ "@remix-run/react": "1.19.3", "@remix-run/serve": "1.19.3", "@thoughtbot/tailwindcss-aria-attributes": "^0.2.0", - "@upstash/redis": "^1.13.1", "clsx": "^1.2.1", "cm6-theme-basic-dark": "^0.2.0", "cm6-theme-basic-light": "^0.2.0", "debounce": "^1.2.1", "front-matter": "^4.0.2", + "ioredis": "^5.3.2", "is-binary-path": "^2.1.0", "markdown-it": "^13.0.1", "markdown-it-anchor": "^8.6.7", @@ -2938,6 +2938,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -4037,14 +4042,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/@upstash/redis": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.13.1.tgz", - "integrity": "sha512-ZuwUFuAH4GP8jCdmLtTQkEOjtspxXIo52FzwL/DqkQuFzhReKKazxEQe9s4hBCd1s4YeynjBIrl0HaqLlSjBwA==", - "dependencies": { - "isomorphic-fetch": "^3.0.0" - } - }, "node_modules/@vanilla-extract/babel-plugin-debug-ids": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@vanilla-extract/babel-plugin-debug-ids/-/babel-plugin-debug-ids-1.0.3.tgz", @@ -5082,6 +5079,14 @@ "node": ">=6" } }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cm6-theme-basic-dark": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/cm6-theme-basic-dark/-/cm6-theme-basic-dark-0.2.0.tgz", @@ -5345,7 +5350,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -5523,6 +5527,14 @@ "node": ">= 14" } }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -7992,6 +8004,29 @@ "node": ">= 0.4" } }, + "node_modules/ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "dependencies": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, "node_modules/ip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", @@ -8497,15 +8532,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "dependencies": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -8787,6 +8813,16 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "node_modules/lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -10016,8 +10052,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -10089,6 +10124,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -11475,6 +11511,25 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", "dev": true }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -12254,6 +12309,11 @@ "node": ">= 8" } }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -12751,7 +12811,8 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "node_modules/trough": { "version": "2.1.0", @@ -13818,17 +13879,14 @@ "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -16084,6 +16142,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "@ioredis/commands": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.2.0.tgz", + "integrity": "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==" + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -16946,14 +17009,6 @@ } } }, - "@upstash/redis": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@upstash/redis/-/redis-1.13.1.tgz", - "integrity": "sha512-ZuwUFuAH4GP8jCdmLtTQkEOjtspxXIo52FzwL/DqkQuFzhReKKazxEQe9s4hBCd1s4YeynjBIrl0HaqLlSjBwA==", - "requires": { - "isomorphic-fetch": "^3.0.0" - } - }, "@vanilla-extract/babel-plugin-debug-ids": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@vanilla-extract/babel-plugin-debug-ids/-/babel-plugin-debug-ids-1.0.3.tgz", @@ -17713,6 +17768,11 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" }, + "cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==" + }, "cm6-theme-basic-dark": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/cm6-theme-basic-dark/-/cm6-theme-basic-dark-0.2.0.tgz", @@ -17905,7 +17965,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -18042,6 +18101,11 @@ "esprima": "^4.0.1" } }, + "denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" + }, "depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -19924,6 +19988,22 @@ "side-channel": "^1.0.4" } }, + "ioredis": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.3.2.tgz", + "integrity": "sha512-1DKMMzlIHM02eBBVOFQ1+AolGjs6+xEcM4PDL7NqOS6szq7H9jSaEkIUH6/a5Hl241LzW6JLSiAbNvTQjUupUA==", + "requires": { + "@ioredis/commands": "^1.1.1", + "cluster-key-slot": "^1.1.0", + "debug": "^4.3.4", + "denque": "^2.1.0", + "lodash.defaults": "^4.2.0", + "lodash.isarguments": "^3.1.0", + "redis-errors": "^1.2.0", + "redis-parser": "^3.0.0", + "standard-as-callback": "^2.1.0" + } + }, "ip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", @@ -20252,15 +20332,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "isomorphic-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz", - "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==", - "requires": { - "node-fetch": "^2.6.1", - "whatwg-fetch": "^3.4.1" - } - }, "iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -20494,6 +20565,16 @@ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "dev": true }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==" + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", @@ -21321,8 +21402,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "mute-stream": { "version": "0.0.8", @@ -21376,6 +21456,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, "requires": { "whatwg-url": "^5.0.0" } @@ -22378,6 +22459,19 @@ } } }, + "redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==" + }, + "redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "requires": { + "redis-errors": "^1.0.0" + } + }, "reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -22986,6 +23080,11 @@ "minipass": "^3.1.1" } }, + "standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==" + }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -23381,7 +23480,8 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true }, "trough": { "version": "2.1.0", @@ -24028,17 +24128,14 @@ "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-fetch": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz", - "integrity": "sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==" + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true }, "whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" diff --git a/package.json b/package.json index ab55c50..f3069a5 100644 --- a/package.json +++ b/package.json @@ -32,12 +32,12 @@ "@remix-run/react": "1.19.3", "@remix-run/serve": "1.19.3", "@thoughtbot/tailwindcss-aria-attributes": "^0.2.0", - "@upstash/redis": "^1.13.1", "clsx": "^1.2.1", "cm6-theme-basic-dark": "^0.2.0", "cm6-theme-basic-light": "^0.2.0", "debounce": "^1.2.1", "front-matter": "^4.0.2", + "ioredis": "^5.3.2", "is-binary-path": "^2.1.0", "markdown-it": "^13.0.1", "markdown-it-anchor": "^8.6.7",