diff --git a/Dockerfile b/Dockerfile index a0bf0019..69cb1b8c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -84,7 +84,7 @@ ENV POSTHOG_PAPIK=$POSTHOG_PAPIK # ENV SOURCEBOT_TELEMETRY_DISABLED=1 # Configure dependencies -RUN apk add --no-cache git ca-certificates bind-tools tini jansson wget supervisor uuidgen curl perl jq +RUN apk add --no-cache git ca-certificates bind-tools tini jansson wget supervisor uuidgen curl perl jq redis # Configure zoekt COPY vendor/zoekt/install-ctags-alpine.sh . diff --git a/package.json b/package.json index a663bf80..4062ebdd 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,14 @@ "scripts": { "build": "yarn workspaces run build", "test": "yarn workspaces run test", - "dev": "npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web", - "dev:mt": "npm-run-all --print-label --parallel dev:zoekt:mt dev:backend dev:web", + "dev": "npm-run-all --print-label --parallel dev:zoekt dev:backend dev:web dev:redis", + "dev:mt": "npm-run-all --print-label --parallel dev:zoekt:mt dev:backend dev:web dev:redis", "dev:zoekt": "export PATH=\"$PWD/bin:$PATH\" && export SRC_TENANT_ENFORCEMENT_MODE=none && zoekt-webserver -index .sourcebot/index -rpc", "dev:zoekt:mt": "export PATH=\"$PWD/bin:$PATH\" && export SRC_TENANT_ENFORCEMENT_MODE=strict && zoekt-webserver -index .sourcebot/index -rpc", "dev:backend": "yarn workspace @sourcebot/backend dev:watch", - "dev:web": "yarn workspace @sourcebot/web dev" + "dev:web": "yarn workspace @sourcebot/web dev", + "dev:redis": "docker ps --filter \"name=redis\" --format \"{{.Names}}\" | grep -q \"^redis$\" && docker rm -f redis; docker run -d --name redis -p 6379:6379 redis" + }, "devDependencies": { "npm-run-all": "^4.1.5" diff --git a/packages/backend/package.json b/packages/backend/package.json index 2514cd85..330b5b1e 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -34,6 +34,8 @@ "posthog-node": "^4.2.1", "simple-git": "^3.27.0", "strip-json-comments": "^5.0.1", - "winston": "^3.15.0" + "winston": "^3.15.0", + "bullmq": "^5.34.10", + "ioredis": "^5.4.2" } } diff --git a/packages/backend/src/constants.ts b/packages/backend/src/constants.ts index 41f091bd..11f82341 100644 --- a/packages/backend/src/constants.ts +++ b/packages/backend/src/constants.ts @@ -6,6 +6,7 @@ import { Settings } from "./types.js"; export const DEFAULT_SETTINGS: Settings = { maxFileSize: 2 * 1024 * 1024, // 2MB in bytes autoDeleteStaleRepos: true, - reindexIntervalMs: 1000 * 60 * 60, // 1 hour in milliseconds + reindexIntervalMs: 1000 * 60, resyncIntervalMs: 1000 * 60 * 60 * 24, // 1 day in milliseconds + indexConcurrencyMultiple: 3, } \ No newline at end of file diff --git a/packages/backend/src/main.ts b/packages/backend/src/main.ts index abc220ad..3b4e8c9f 100644 --- a/packages/backend/src/main.ts +++ b/packages/backend/src/main.ts @@ -1,4 +1,4 @@ -import { PrismaClient, Repo } from '@sourcebot/db'; +import { PrismaClient, Repo, RepoIndexingStatus } from '@sourcebot/db'; import { existsSync, watch } from 'fs'; import { syncConfig } from "./config.js"; import { cloneRepository, fetchRepository } from "./git.js"; @@ -8,6 +8,9 @@ import { AppContext } from "./types.js"; import { getRepoPath, isRemotePath, measure } from "./utils.js"; import { indexGitRepository } from "./zoekt.js"; import { DEFAULT_SETTINGS } from './constants.js'; +import { Queue, Worker, Job } from 'bullmq'; +import { Redis } from 'ioredis'; +import * as os from 'os'; const logger = createLogger('main'); @@ -53,6 +56,23 @@ const syncGitRepository = async (repo: Repo, ctx: AppContext) => { } } +async function addReposToQueue(db: PrismaClient, queue: Queue, repos: Repo[]) { + for (const repo of repos) { + await db.$transaction(async (tx) => { + await tx.repo.update({ + where: { id: repo.id }, + data: { repoIndexingStatus: RepoIndexingStatus.IN_INDEX_QUEUE }, + }); + + // Add the job to the queue + await queue.add('indexJob', repo); + logger.info(`Added job to queue for repo ${repo.id}`); + }).catch((err) => { + logger.error(`Failed to add job to queue for repo ${repo.id}: ${err}`); + }); + } +} + export const main = async (db: PrismaClient, context: AppContext) => { let abortController = new AbortController(); let isSyncing = false; @@ -97,50 +117,90 @@ export const main = async (db: PrismaClient, context: AppContext) => { // Sync immediately on startup await _syncConfig(); - while (true) { - const repos = await db.repo.findMany(); - - for (const repo of repos) { - const lastIndexed = repo.indexedAt ?? new Date(0); - - if (lastIndexed.getTime() > (Date.now() - DEFAULT_SETTINGS.reindexIntervalMs)) { - continue; - } + const redis = new Redis({ + host: 'localhost', + port: 6379, + maxRetriesPerRequest: null + }); + redis.ping().then(() => { + logger.info('Connected to redis'); + }).catch((err) => { + logger.error('Failed to connect to redis'); + console.error(err); + process.exit(1); + }); + + const indexQueue = new Queue('indexQueue'); + + const numCores = os.cpus().length; + const numWorkers = numCores * DEFAULT_SETTINGS.indexConcurrencyMultiple; + logger.info(`Detected ${numCores} cores. Setting max concurrency to ${numWorkers}`); + const worker = new Worker('indexQueue', async (job) => { + const repo = job.data as Repo; + + let indexDuration_s: number | undefined; + let fetchDuration_s: number | undefined; + let cloneDuration_s: number | undefined; + + const stats = await syncGitRepository(repo, context); + indexDuration_s = stats.indexDuration_s; + fetchDuration_s = stats.fetchDuration_s; + cloneDuration_s = stats.cloneDuration_s; + + captureEvent('repo_synced', { + vcs: 'git', + codeHost: repo.external_codeHostType, + indexDuration_s, + fetchDuration_s, + cloneDuration_s, + }); - try { - let indexDuration_s: number | undefined; - let fetchDuration_s: number | undefined; - let cloneDuration_s: number | undefined; - - const stats = await syncGitRepository(repo, context); - indexDuration_s = stats.indexDuration_s; - fetchDuration_s = stats.fetchDuration_s; - cloneDuration_s = stats.cloneDuration_s; - - captureEvent('repo_synced', { - vcs: 'git', - codeHost: repo.external_codeHostType, - indexDuration_s, - fetchDuration_s, - cloneDuration_s, - }); - } catch (err: any) { - // @todo : better error handling here.. - logger.error(err); - continue; + await db.repo.update({ + where: { + id: repo.id, + }, + data: { + indexedAt: new Date(), + repoIndexingStatus: RepoIndexingStatus.INDEXED, } - + }); + }, { connection: redis, concurrency: numWorkers }); + + worker.on('completed', (job) => { + logger.info(`Job ${job.id} completed`); + }); + worker.on('failed', async (job: Job | undefined, err) => { + logger.info(`Job failed with error: ${err}`); + if (job) { await db.repo.update({ where: { - id: repo.id, + id: job.data.id, }, data: { - indexedAt: new Date(), + repoIndexingStatus: RepoIndexingStatus.FAILED, } - }); + }) } + }); - await new Promise(resolve => setTimeout(resolve, 1000)); + while (true) { + const thresholdDate = new Date(Date.now() - DEFAULT_SETTINGS.reindexIntervalMs); + const repos = await db.repo.findMany({ + where: { + repoIndexingStatus: { + notIn: [RepoIndexingStatus.IN_INDEX_QUEUE, RepoIndexingStatus.FAILED] + }, + OR: [ + { indexedAt: null }, + { indexedAt: { lt: thresholdDate } }, + { repoIndexingStatus: RepoIndexingStatus.NEW } + ] + } + }); + logger.info(`Found ${repos.length} repos to index...`); + addReposToQueue(db, indexQueue, repos); + + await new Promise(resolve => setTimeout(resolve, 1000)); } } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 54563413..05bd9639 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -74,6 +74,10 @@ export type Settings = { * The interval (in milliseconds) at which the configuration file should be re-synced. */ resyncIntervalMs: number; + /** + * The multiple of the number of CPUs to use for indexing. + */ + indexConcurrencyMultiple: number; } // @see : https://stackoverflow.com/a/61132308 diff --git a/packages/db/prisma/migrations/20250115221611_add_indexing_status_to_repo/migration.sql b/packages/db/prisma/migrations/20250115221611_add_indexing_status_to_repo/migration.sql new file mode 100644 index 00000000..1531290d --- /dev/null +++ b/packages/db/prisma/migrations/20250115221611_add_indexing_status_to_repo/migration.sql @@ -0,0 +1,25 @@ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Repo" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "name" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + "indexedAt" DATETIME, + "isFork" BOOLEAN NOT NULL, + "isArchived" BOOLEAN NOT NULL, + "metadata" JSONB NOT NULL, + "cloneUrl" TEXT NOT NULL, + "tenantId" INTEGER NOT NULL, + "repoIndexingStatus" TEXT NOT NULL DEFAULT 'NEW', + "external_id" TEXT NOT NULL, + "external_codeHostType" TEXT NOT NULL, + "external_codeHostUrl" TEXT NOT NULL +); +INSERT INTO "new_Repo" ("cloneUrl", "createdAt", "external_codeHostType", "external_codeHostUrl", "external_id", "id", "indexedAt", "isArchived", "isFork", "metadata", "name", "tenantId", "updatedAt") SELECT "cloneUrl", "createdAt", "external_codeHostType", "external_codeHostUrl", "external_id", "id", "indexedAt", "isArchived", "isFork", "metadata", "name", "tenantId", "updatedAt" FROM "Repo"; +DROP TABLE "Repo"; +ALTER TABLE "new_Repo" RENAME TO "Repo"; +CREATE UNIQUE INDEX "Repo_external_id_external_codeHostUrl_key" ON "Repo"("external_id", "external_codeHostUrl"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index ea4ca1e3..96c97462 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -10,6 +10,14 @@ datasource db { url = env("DATABASE_URL") } +enum RepoIndexingStatus { + NEW + IN_INDEX_QUEUE + INDEXING + INDEXED + FAILED +} + model Repo { id Int @id @default(autoincrement()) name String @@ -22,6 +30,8 @@ model Repo { cloneUrl String tenantId Int + repoIndexingStatus RepoIndexingStatus @default(NEW) + // The id of the repo in the external service external_id String // The type of the external service (e.g., github, gitlab, etc.) diff --git a/supervisord.conf b/supervisord.conf index 246a618c..7059ec61 100644 --- a/supervisord.conf +++ b/supervisord.conf @@ -29,4 +29,13 @@ autorestart=true startretries=3 stdout_logfile=/dev/fd/1 stdout_logfile_maxbytes=0 +redirect_stderr=true + +[program:redis] +command=redis-server +autostart=true +autorestart=true +startretries=3 +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 redirect_stderr=true \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index ce99676c..f84fbc20 100644 --- a/yarn.lock +++ b/yarn.lock @@ -849,6 +849,11 @@ resolved "https://registry.yarnpkg.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz#56f00962ff0c4e0eb93d34a047d29fa995e3e342" integrity sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg== +"@ioredis/commands@^1.1.1": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ioredis/commands/-/commands-1.2.0.tgz#6d61b3097470af1fdbbe622795b8921d42018e11" + integrity sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -1059,6 +1064,36 @@ "@lezer/highlight" "^1.0.0" "@lezer/lr" "^1.4.0" +"@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz#9edec61b22c3082018a79f6d1c30289ddf3d9d11" + integrity sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw== + +"@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz#33677a275204898ad8acbf62734fc4dc0b6a4855" + integrity sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw== + +"@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz#19edf7cdc2e7063ee328403c1d895a86dd28f4bb" + integrity sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg== + +"@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz#94fb0543ba2e28766c3fc439cabbe0440ae70159" + integrity sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw== + +"@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz#4a0609ab5fe44d07c9c60a11e4484d3c38bbd6e3" + integrity sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg== + +"@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz#0aa5502d547b57abfc4ac492de68e2006e417242" + integrity sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ== + "@next/env@14.2.21": version "14.2.21" resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.21.tgz#09ff0813d29c596397e141205d4f5fd5c236bdd0" @@ -2477,6 +2512,19 @@ buffer@^6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" +bullmq@^5.34.10: + version "5.34.10" + resolved "https://registry.yarnpkg.com/bullmq/-/bullmq-5.34.10.tgz#9a8d30a071cbb413cc1d5d478879721d54f49cb2" + integrity sha512-ia6EzpQm1ZPq6GUBSLyfvzJrhdBTd1f3Gn2g9pFtLX4hBOob6QHmcmBzGgPlSCyr/i2Qfe4OdjS21bRd02srbw== + dependencies: + cron-parser "^4.9.0" + ioredis "^5.4.1" + msgpackr "^1.11.2" + node-abort-controller "^3.1.1" + semver "^7.5.4" + tslib "^2.0.0" + uuid "^9.0.0" + busboy@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" @@ -2585,6 +2633,11 @@ clsx@^2.1.1: resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== +cluster-key-slot@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + cm6-graphql@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/cm6-graphql/-/cm6-graphql-0.2.0.tgz#f73880eeed63d0bbe331f14c8220f2265c6735fd" @@ -2803,6 +2856,13 @@ crelt@^1.0.5: resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72" integrity sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g== +cron-parser@^4.9.0: + version "4.9.0" + resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-4.9.0.tgz#0340694af3e46a0894978c6f52a6dbb5c0f11ad5" + integrity sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q== + dependencies: + luxon "^3.2.1" + cross-fetch@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-4.0.0.tgz#f037aef1580bb3a1a35164ea2a848ba81b445983" @@ -2968,7 +3028,12 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -detect-libc@^2.0.3: +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + +detect-libc@^2.0.1, detect-libc@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== @@ -3990,6 +4055,21 @@ invariant@^2.2.4: dependencies: loose-envify "^1.0.0" +ioredis@^5.4.1, ioredis@^5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.4.2.tgz#ebb6f1a10b825b2c0fb114763d7e82114a0bee6c" + integrity sha512-0SZXGNGZ+WzISQ67QDyZ2x0+wVxjjUndtD8oSeik/4ajifeiRufed8fCb8QW8VMyi4MXcS+UO1k/0NGhvq1PAg== + 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" + is-arguments@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" @@ -4434,6 +4514,16 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.defaults@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== + +lodash.isarguments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" + integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -4490,6 +4580,11 @@ lucide-react@^0.435.0: resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.435.0.tgz#88c5cc6de61b89e42cbef309a38f100deee1bb32" integrity sha512-we5GKfzjMDw9m9SsyZJvWim9qaT+Ya5kaRS+OGFqgLqXUrPM1h+7CiMw5pKdEIoaBqfXz2pyv9TASAdpIAJs0Q== +luxon@^3.2.1: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== + magic-string@^0.30.12: version "0.30.12" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.12.tgz#9eb11c9d072b9bcb4940a5b2c2e1a217e4ee1a60" @@ -4575,6 +4670,27 @@ ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msgpackr-extract@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz#e9d87023de39ce714872f9e9504e3c1996d61012" + integrity sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA== + dependencies: + node-gyp-build-optional-packages "5.2.2" + optionalDependencies: + "@msgpackr-extract/msgpackr-extract-darwin-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-darwin-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-arm64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-linux-x64" "3.0.3" + "@msgpackr-extract/msgpackr-extract-win32-x64" "3.0.3" + +msgpackr@^1.11.2: + version "1.11.2" + resolved "https://registry.yarnpkg.com/msgpackr/-/msgpackr-1.11.2.tgz#4463b7f7d68f2e24865c395664973562ad24473d" + integrity sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g== + optionalDependencies: + msgpackr-extract "^3.0.2" + mz@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32" @@ -4627,6 +4743,11 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-cleanup@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/node-cleanup/-/node-cleanup-2.1.2.tgz#7ac19abd297e09a7f72a71545d951b517e4dde2c" @@ -4639,6 +4760,13 @@ node-fetch@^2.6.12: dependencies: whatwg-url "^5.0.0" +node-gyp-build-optional-packages@5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz#522f50c2d53134d7f3a76cd7255de4ab6c96a3a4" + integrity sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw== + dependencies: + detect-libc "^2.0.1" + normalize-package-data@^2.3.2: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -5203,6 +5331,18 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +redis-errors@^1.0.0, redis-errors@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/redis-errors/-/redis-errors-1.2.0.tgz#eb62d2adb15e4eaf4610c04afe1529384250abad" + integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== + +redis-parser@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/redis-parser/-/redis-parser-3.0.0.tgz#b66d828cdcafe6b4b8a428a7def4c6bcac31c8b4" + integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== + dependencies: + redis-errors "^1.0.0" + reflect.getprototypeof@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz#3ab04c32a8390b770712b7a8633972702d278859" @@ -5552,6 +5692,11 @@ stackback@0.0.2: resolved "https://registry.yarnpkg.com/stackback/-/stackback-0.0.2.tgz#1ac8a0d9483848d1695e418b6d031a3c3ce68e3b" integrity sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw== +standard-as-callback@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" + integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== + std-env@^3.7.0, std-env@^3.8.0: version "3.8.0" resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.8.0.tgz#b56ffc1baf1a29dcc80a3bdf11d7fca7c315e7d5" @@ -6097,6 +6242,11 @@ util-deprecate@^1.0.1, util-deprecate@^1.0.2: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +uuid@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" + integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"