diff --git a/.changeset/fair-rabbits-cross.md b/.changeset/fair-rabbits-cross.md new file mode 100644 index 000000000..e887917db --- /dev/null +++ b/.changeset/fair-rabbits-cross.md @@ -0,0 +1,5 @@ +--- +"@blobscan/syncers": minor +--- + +Added swarm stamp syncer diff --git a/package.json b/package.json index 53021d87f..87753bbd3 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "@vitest/coverage-v8": "^0.34.3", "@vitest/ui": "^0.34.1", "dotenv-cli": "^7.2.1", + "msw": "^2.3.1", "prettier": "^2.8.8", "prettier-plugin-tailwindcss": "^0.2.8", "ts-node": "^10.9.1", diff --git a/packages/syncers/package.json b/packages/syncers/package.json index 01eafe66e..039fa09a7 100644 --- a/packages/syncers/package.json +++ b/packages/syncers/package.json @@ -17,6 +17,8 @@ "@blobscan/dayjs": "workspace:^0.0.2", "@blobscan/db": "workspace:^0.7.0", "@blobscan/logger": "workspace:^0.1.0", + "@blobscan/zod": "workspace:^0.1.0", + "axios": "^1.7.2", "bullmq": "^4.13.2", "ioredis": "^5.3.2" }, diff --git a/packages/syncers/src/errors.ts b/packages/syncers/src/errors.ts index 6afc5bf78..8967ff33d 100644 --- a/packages/syncers/src/errors.ts +++ b/packages/syncers/src/errors.ts @@ -1,3 +1,7 @@ +import type { AxiosError } from "axios"; + +import { z } from "@blobscan/zod"; + export class ErrorException extends Error { constructor(message: string, cause?: unknown) { super(message, { @@ -13,3 +17,34 @@ export class SyncerError extends ErrorException { super(`Syncer "${syncerName}" failed: ${message}`, cause); } } + +const swarmApiResponseErrorSchema = z.object({ + code: z.number(), + message: z.string(), + reasons: z.array(z.unknown()).optional(), +}); + +export class SwarmNodeError extends ErrorException { + code: number | undefined; + reasons?: unknown[]; + + constructor(error: AxiosError) { + let message: string; + let code: number | undefined; + const result = swarmApiResponseErrorSchema.safeParse(error.response?.data); + let reasons: unknown[] | undefined; + + if (result.success) { + code = result.data.code; + message = result.data.message; + reasons = result.data.reasons; + } else { + message = error.message; + } + + super(message, error.cause); + + this.code = code; + this.reasons = reasons; + } +} diff --git a/packages/syncers/src/syncers/SwarmStampSyncer.ts b/packages/syncers/src/syncers/SwarmStampSyncer.ts new file mode 100644 index 000000000..d17822297 --- /dev/null +++ b/packages/syncers/src/syncers/SwarmStampSyncer.ts @@ -0,0 +1,73 @@ +import type { AxiosResponse } from "axios"; +import { AxiosError } from "axios"; +import axios from "axios"; + +import { prisma } from "@blobscan/db"; + +import { BaseSyncer } from "../BaseSyncer"; +import type { CommonSyncerConfig } from "../BaseSyncer"; +import { SwarmNodeError } from "../errors"; + +type BatchData = { + batchID: string; + batchTTL: number; +}; + +export interface SwarmStampSyncerConfig extends CommonSyncerConfig { + beeEndpoint: string; + batchId: string; +} + +export class SwarmStampSyncer extends BaseSyncer { + constructor({ + cronPattern, + redisUriOrConnection, + batchId, + beeEndpoint, + }: SwarmStampSyncerConfig) { + const name = "swarm-stamp"; + super({ + name, + cronPattern, + redisUriOrConnection, + syncerFn: async () => { + let response: AxiosResponse; + + try { + const url = `${beeEndpoint}/stamps/${batchId}`; + + response = await axios.get(url); + } catch (err) { + let cause; + + if (err instanceof AxiosError) { + cause = new SwarmNodeError(err); + } + + throw new Error(`Failed to fetch stamp batch "${batchId}"`, { + cause, + }); + } + + const { batchTTL } = response.data; + + await prisma.blobStoragesState.upsert({ + create: { + swarmDataId: batchId, + swarmDataTTL: batchTTL, + }, + update: { + swarmDataTTL: batchTTL, + updatedAt: new Date(), + }, + where: { + id: 1, + swarmDataId: batchId, + }, + }); + + this.logger.info(`Swarm stamp data with batch ID "${batchId}" updated`); + }, + }); + } +} diff --git a/packages/syncers/test/SwarmStampSyncer.test.ts b/packages/syncers/test/SwarmStampSyncer.test.ts new file mode 100644 index 000000000..100fbe5a8 --- /dev/null +++ b/packages/syncers/test/SwarmStampSyncer.test.ts @@ -0,0 +1,183 @@ +/* eslint-disable @typescript-eslint/no-misused-promises */ +import { http, HttpResponse } from "msw"; +import { setupServer } from "msw/node"; +import { beforeAll, beforeEach, describe, expect, it } from "vitest"; + +import type { BlobStoragesState } from "@blobscan/db"; +import { prisma } from "@blobscan/db"; +import { fixtures, testValidError } from "@blobscan/test"; + +import type { SwarmStampSyncerConfig } from "../src/syncers/SwarmStampSyncer"; +import { SwarmStampSyncer } from "../src/syncers/SwarmStampSyncer"; + +const BEE_ENDPOINT = process.env.BEE_ENDPOINT ?? "http://localhost:1633"; + +class SwarmStampSyncerMock extends SwarmStampSyncer { + constructor({ batchId, cronPattern }: Partial = {}) { + super({ + redisUriOrConnection: process.env.REDIS_URI ?? "", + cronPattern: cronPattern ?? "* * * * *", + batchId: batchId ?? process.env.SWARM_BATCH_ID ?? "", + beeEndpoint: BEE_ENDPOINT, + }); + } + + getQueue() { + return this.queue; + } + + getWorkerProcessor() { + return this.syncerFn; + } +} + +describe("SwarmStampSyncer", () => { + const expectedBatchId = fixtures.blobStoragesState[0]?.swarmDataId as string; + const expectedBatchTTL = 1000; + + let swarmStampSyncer: SwarmStampSyncerMock; + + beforeAll(() => { + const baseUrl = `${BEE_ENDPOINT}/stamps`; + const server = setupServer( + ...[ + http.get(`${baseUrl}/:batchId`, ({ request }) => { + const batchId = request.url.split("/").pop(); + + if (!batchId || batchId.length !== 64) { + return HttpResponse.json( + { + code: 400, + message: "invalid path params", + reasons: [ + { + field: "batch_id", + error: "odd length hex string", + }, + ], + }, + { status: 400 } + ); + } + + if (batchId !== expectedBatchId) { + return HttpResponse.json( + { + code: 404, + message: "issuer does not exist", + }, + { status: 404 } + ); + } + + return HttpResponse.json( + { + batchID: expectedBatchId, + batchTTL: expectedBatchTTL, + }, + { + status: 200, + } + ); + }), + ] + ); + + server.listen(); + + return () => { + server.close(); + }; + }); + + beforeEach(() => { + swarmStampSyncer = new SwarmStampSyncerMock(); + + return async () => { + await swarmStampSyncer.close(); + }; + }); + + describe("when creating a new swarm batch data row in the db", async () => { + let blobStorageState: BlobStoragesState | null = null; + + beforeEach(async () => { + await prisma.blobStoragesState.deleteMany(); + + const workerProcessor = swarmStampSyncer.getWorkerProcessor(); + + await workerProcessor().catch((err) => console.log(err)); + + blobStorageState = await prisma.blobStoragesState.findFirst(); + }); + + it("should create the row", async () => { + expect(blobStorageState).toBeDefined(); + }); + + it("should create a new swarm data entry on the DB", async () => { + expect(blobStorageState?.swarmDataId).toBe(process.env.SWARM_BATCH_ID); + }); + + it("should create a new swarm data entry on the DB with the correct batch TTL", async () => { + expect(blobStorageState?.swarmDataTTL).toBe(expectedBatchTTL); + }); + }); + + it("should update the batch TTl", async () => { + await prisma.blobStoragesState.update({ + data: { + swarmDataTTL: 99999, + }, + where: { + id: 1, + }, + }); + + const workerProcessor = swarmStampSyncer.getWorkerProcessor(); + await workerProcessor(); + + const blobStorageState = await prisma.blobStoragesState.findFirst(); + + expect(blobStorageState?.swarmDataTTL).toBe(expectedBatchTTL); + }); + + testValidError( + "should fail when trying to fetch a non-existing batch", + async () => { + const failingSwarmStampSyncer = new SwarmStampSyncerMock({ + batchId: + "6b538866048cfb6e9e1d06805374c51572c11219d2d550c03e6e277366cb0371", + }); + const failingWorkerProcessor = + failingSwarmStampSyncer.getWorkerProcessor(); + + await failingWorkerProcessor().finally(async () => { + await failingSwarmStampSyncer.close(); + }); + }, + Error, + { + checkCause: true, + } + ); + + testValidError( + "should fail when trying to fetch an invalid batch", + async () => { + const failingSwarmStampSyncer = new SwarmStampSyncerMock({ + batchId: "invalid-batch", + }); + const failingWorkerProcessor = + failingSwarmStampSyncer.getWorkerProcessor(); + + await failingWorkerProcessor().finally(async () => { + await failingSwarmStampSyncer.close(); + }); + }, + Error, + { + checkCause: true, + } + ); +}); diff --git a/packages/syncers/test/SwarmSyncer.test.ts b/packages/syncers/test/SwarmSyncer.test.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/syncers/test/__snapshots__/SwarmStampSyncer.test.ts.snap b/packages/syncers/test/__snapshots__/SwarmStampSyncer.test.ts.snap new file mode 100644 index 000000000..8385c3d78 --- /dev/null +++ b/packages/syncers/test/__snapshots__/SwarmStampSyncer.test.ts.snap @@ -0,0 +1,9 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`SwarmStampSyncer > should fail when trying to fetch a non-existing batch 1`] = `"Failed to fetch stamp batch \\"6b538866048cfb6e9e1d06805374c51572c11219d2d550c03e6e277366cb0371\\""`; + +exports[`SwarmStampSyncer > should fail when trying to fetch a non-existing batch 2`] = `[SwarmNodeError: issuer does not exist]`; + +exports[`SwarmStampSyncer > should fail when trying to fetch an invalid batch 1`] = `"Failed to fetch stamp batch \\"invalid-batch\\""`; + +exports[`SwarmStampSyncer > should fail when trying to fetch an invalid batch 2`] = `[SwarmNodeError: invalid path params]`; diff --git a/packages/syncers/test/helpers.ts b/packages/syncers/test/helpers.ts new file mode 100644 index 000000000..0c79d5a59 --- /dev/null +++ b/packages/syncers/test/helpers.ts @@ -0,0 +1,11 @@ +import { setupServer } from "msw/node"; + +export function createServer(handlers: Parameters[0][]) { + const server = setupServer(...handlers); + + server.listen(); + + return () => { + server.close(); + }; +} diff --git a/packages/test/src/fixtures/index.ts b/packages/test/src/fixtures/index.ts index b96e6fed0..5a31ad036 100644 --- a/packages/test/src/fixtures/index.ts +++ b/packages/test/src/fixtures/index.ts @@ -1,9 +1,5 @@ -import { - BlobData, - BlobDataStorageReference, - type PrismaClient, - type Rollup, -} from "@prisma/client"; +import type { BlobData, BlobDataStorageReference } from "@prisma/client"; +import type { PrismaClient, Rollup } from "@prisma/client"; import POSTGRES_DATA from "./postgres/data.json"; diff --git a/packages/test/src/fixtures/postgres/data.json b/packages/test/src/fixtures/postgres/data.json index f62b1561b..a227867c2 100644 --- a/packages/test/src/fixtures/postgres/data.json +++ b/packages/test/src/fixtures/postgres/data.json @@ -12,7 +12,7 @@ "blobStoragesState": [ { "id": 1, - "swarmDataId": "batch-1", + "swarmDataId": "f89e63edf757f06e89933761d6d46592d03026efb9871f9d244f34da86b6c242", "swarmDataTTL": 1000, "updatedAt": "2023-10-31T12:10:00Z" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68830c843..940d5e137 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: dotenv-cli: specifier: ^7.2.1 version: 7.4.1 + msw: + specifier: ^2.3.1 + version: 2.3.1(typescript@5.4.5) prettier: specifier: ^2.8.8 version: 2.8.8 @@ -103,7 +106,7 @@ importers: version: 2.2.1 '@tailwindcss/typography': specifier: ^0.5.7 - version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5))) + version: 0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5))) autoprefixer: specifier: ^10.4.14 version: 10.4.19(postcss@8.4.38) @@ -142,7 +145,7 @@ importers: version: 1.2.1 tailwindcss: specifier: ^3.3.1 - version: 3.4.3(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)) + version: 3.4.3(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) devDependencies: eslint: specifier: ^8.45.0 @@ -598,6 +601,12 @@ importers: '@blobscan/logger': specifier: workspace:^0.1.0 version: link:../logger + '@blobscan/zod': + specifier: workspace:^0.1.0 + version: link:../zod + axios: + specifier: ^1.7.2 + version: 1.7.2 bullmq: specifier: ^4.13.2 version: 4.17.0 @@ -675,7 +684,7 @@ importers: version: 8.4.38 tailwindcss: specifier: ^3.3.1 - version: 3.4.3(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)) + version: 3.4.3(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) tooling/typescript: {} @@ -1391,6 +1400,12 @@ packages: '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + '@bundled-es-modules/cookie@2.0.0': + resolution: {integrity: sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==} + + '@bundled-es-modules/statuses@1.0.1': + resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + '@changesets/apply-release-plan@7.0.0': resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==} @@ -1791,6 +1806,22 @@ packages: '@vue/compiler-sfc': optional: true + '@inquirer/confirm@3.1.9': + resolution: {integrity: sha512-UF09aejxCi4Xqm6N/jJAiFXArXfi9al52AFaSD+2uIHnhZGtd1d6lIGTRMPouVSJxbGEi+HkOWSYaiEY/+szUw==} + engines: {node: '>=18'} + + '@inquirer/core@8.2.2': + resolution: {integrity: sha512-K8SuNX45jEFlX3EBJpu9B+S2TISzMPGXZIuJ9ME924SqbdW6Pt6fIkKvXg7mOEOKJ4WxpQsxj0UTfcL/A434Ww==} + engines: {node: '>=18'} + + '@inquirer/figures@1.0.3': + resolution: {integrity: sha512-ErXXzENMH5pJt5/ssXV0DfWUZqly8nGzf0UcBV9xTnP+KyffE2mqyxIMBrZ8ijQck2nU0TQm40EQB53YreyWHw==} + engines: {node: '>=18'} + + '@inquirer/type@1.3.3': + resolution: {integrity: sha512-xTUt0NulylX27/zMx04ZYar/kr1raaiFTVvQ5feljQsiAgdm0WPj4S73/ye0fbslh+15QrIuDvfCXTek7pMY5A==} + engines: {node: '>=18'} + '@ioredis/commands@1.2.0': resolution: {integrity: sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg==} @@ -1898,6 +1929,14 @@ packages: cpu: [x64] os: [win32] + '@mswjs/cookies@1.1.0': + resolution: {integrity: sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw==} + engines: {node: '>=18'} + + '@mswjs/interceptors@0.29.1': + resolution: {integrity: sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==} + engines: {node: '>=18'} + '@next-auth/prisma-adapter@1.0.7': resolution: {integrity: sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==} peerDependencies: @@ -2063,6 +2102,15 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + '@opentelemetry/api-logs@0.41.2': resolution: {integrity: sha512-JEV2RAqijAFdWeT6HddYymfnkiRu2ASxoTBr4WsnGJhOjWZkEy6vp+Sx9ozr1NaIODOa2HUyckExIqQjn6qywQ==} engines: {node: '>=14'} @@ -2765,6 +2813,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cors@2.8.17': resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} @@ -2825,12 +2876,18 @@ packages: '@types/morgan@1.9.9': resolution: {integrity: sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==} + '@types/mute-stream@0.0.4': + resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} '@types/node@18.19.31': resolution: {integrity: sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==} + '@types/node@20.14.2': + resolution: {integrity: sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2876,6 +2933,9 @@ packages: '@types/shimmer@1.0.5': resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==} + '@types/statuses@2.0.5': + resolution: {integrity: sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==} + '@types/strip-bom@3.0.0': resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} @@ -2888,6 +2948,9 @@ packages: '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} + '@types/wrap-ansi@3.0.0': + resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} + '@types/zrender@4.0.6': resolution: {integrity: sha512-1jZ9bJn2BsfmYFPBHtl5o3uV+ILejAtGrDcYSpT4qaVKEI/0YY+arw3XHU04Ebd8Nca3SQ7uNcLaqiL+tTFVMg==} @@ -3058,6 +3121,10 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3182,6 +3249,9 @@ packages: axios@1.6.8: resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} + axios@1.7.2: + resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + axobject-query@3.2.1: resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} @@ -3397,6 +3467,14 @@ packages: classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + client-only@0.0.1: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} @@ -4328,6 +4406,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql@16.8.2: + resolution: {integrity: sha512-cvVIBILwuoSyD54U4cF/UXDh5yAobhNV/tPygI4lZhgOIJQE/WLWC4waBRb4I6bDVYb3OVx3lfHbaQOEoUD5sg==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + gtoken@6.1.2: resolution: {integrity: sha512-4ccGpzz7YAr7lxrT2neugmXQ3hP9ho2gcaityLVkiUecAiwiy60Ii8gRbZeOsXV19fYaRjgBSshs8kXw+NKCPQ==} engines: {node: '>=12.0.0'} @@ -4376,6 +4458,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + headers-polyfill@4.0.3: + resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==} + hexer@1.5.0: resolution: {integrity: sha512-dyrPC8KzBzUJ19QTIo1gXNqIISRXQ0NwteW6OeQHRN4ZuZeHkdODfj0zHBdOlHbRY8GqbqK57C9oWSvQZizFsg==} engines: {node: '>= 0.10.x'} @@ -4545,6 +4630,9 @@ packages: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + is-number-object@1.0.7: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} @@ -5049,9 +5137,23 @@ packages: msgpackr@1.10.1: resolution: {integrity: sha512-r5VRLv9qouXuLiIBrLpl2d5ZvPt8svdQTl5/vMvE4nzDMyEX4sgW5yWhuBBj5UmgwOTWj8CIdSXn5sAfsHAWIQ==} + msw@2.3.1: + resolution: {integrity: sha512-ocgvBCLn/5l3jpl1lssIb3cniuACJLoOfZu01e3n5dbJrpA5PeeWn28jCLgQDNt6d7QT8tF2fYRzm9JoEHtiig==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.7.x' + peerDependenciesMeta: + typescript: + optional: true + multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -5276,6 +5378,9 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + outvariant@1.4.2: + resolution: {integrity: sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==} + p-cancelable@3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} @@ -5351,6 +5456,9 @@ packages: path-to-regexp@0.1.7: resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} + path-to-regexp@6.2.2: + resolution: {integrity: sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -5894,6 +6002,10 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + simple-concat@1.0.1: resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} @@ -5994,6 +6106,9 @@ packages: streamx@2.16.1: resolution: {integrity: sha512-m9QYj6WygWyWa3H1YY69amr4nVgy61xfjys7xO7kviL5rfIEc2naf+ewFiOA+aEJD7y0JO3h2GoiUv4TDwEGzQ==} + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + string-template@0.2.1: resolution: {integrity: sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==} @@ -6341,6 +6456,10 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + type-fest@0.6.0: resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} engines: {node: '>=8'} @@ -6353,6 +6472,10 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} + type-fest@4.20.0: + resolution: {integrity: sha512-MBh+PHUHHisjXf4tlx0CFWoMdjx8zCMLJHOjnV1prABYZFHqtFOyauCIK2/7w4oIfwkF8iNhLtnJEfVY2vn3iw==} + engines: {node: '>=16'} + type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -7615,6 +7738,14 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} + '@bundled-es-modules/cookie@2.0.0': + dependencies: + cookie: 0.5.0 + + '@bundled-es-modules/statuses@1.0.1': + dependencies: + statuses: 2.0.1 + '@changesets/apply-release-plan@7.0.0': dependencies: '@babel/runtime': 7.23.2 @@ -8255,6 +8386,31 @@ snapshots: transitivePeerDependencies: - supports-color + '@inquirer/confirm@3.1.9': + dependencies: + '@inquirer/core': 8.2.2 + '@inquirer/type': 1.3.3 + + '@inquirer/core@8.2.2': + dependencies: + '@inquirer/figures': 1.0.3 + '@inquirer/type': 1.3.3 + '@types/mute-stream': 0.0.4 + '@types/node': 20.14.2 + '@types/wrap-ansi': 3.0.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-spinners: 2.9.2 + cli-width: 4.1.0 + mute-stream: 1.0.0 + signal-exit: 4.1.0 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + '@inquirer/figures@1.0.3': {} + + '@inquirer/type@1.3.3': {} + '@ioredis/commands@1.2.0': {} '@istanbuljs/schema@0.1.3': {} @@ -8368,6 +8524,17 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.2': optional: true + '@mswjs/cookies@1.1.0': {} + + '@mswjs/interceptors@0.29.1': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.2 + strict-event-emitter: 0.5.1 + '@next-auth/prisma-adapter@1.0.7(@prisma/client@5.13.0(prisma@5.13.0))(next-auth@4.24.7(next@13.5.6(@babel/core@7.24.5)(@opentelemetry/api@1.8.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react-dom@18.2.0(react@18.2.0))(react@18.2.0))': dependencies: '@prisma/client': 5.13.0(prisma@5.13.0) @@ -8470,6 +8637,15 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.2 + + '@open-draft/until@2.1.0': {} + '@opentelemetry/api-logs@0.41.2': dependencies: '@opentelemetry/api': 1.8.0 @@ -9177,13 +9353,13 @@ snapshots: mini-svg-data-uri: 1.4.4 tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)) - '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)))': + '@tailwindcss/typography@0.5.13(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@18.19.31)(typescript@5.4.5)) + tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) '@tanstack/query-core@4.36.1': {} @@ -9258,6 +9434,8 @@ snapshots: dependencies: '@types/node': 18.19.31 + '@types/cookie@0.6.0': {} + '@types/cors@2.8.17': dependencies: '@types/node': 18.19.31 @@ -9330,12 +9508,20 @@ snapshots: dependencies: '@types/node': 18.19.31 + '@types/mute-stream@0.0.4': + dependencies: + '@types/node': 18.19.31 + '@types/node@12.20.55': {} '@types/node@18.19.31': dependencies: undici-types: 5.26.5 + '@types/node@20.14.2': + dependencies: + undici-types: 5.26.5 + '@types/normalize-package-data@2.4.4': {} '@types/prettier@2.7.3': {} @@ -9383,6 +9569,8 @@ snapshots: '@types/shimmer@1.0.5': {} + '@types/statuses@2.0.5': {} + '@types/strip-bom@3.0.0': {} '@types/strip-json-comments@0.0.30': {} @@ -9394,6 +9582,8 @@ snapshots: '@types/triple-beam@1.3.5': {} + '@types/wrap-ansi@3.0.0': {} + '@types/zrender@4.0.6': {} '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)': @@ -9642,6 +9832,10 @@ snapshots: ansi-colors@4.1.3: {} + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + ansi-regex@5.0.1: {} ansi-styles@3.2.1: @@ -9781,6 +9975,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.7.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axobject-query@3.2.1: dependencies: dequal: 2.0.3 @@ -10045,6 +10247,10 @@ snapshots: classnames@2.5.1: {} + cli-spinners@2.9.2: {} + + cli-width@4.1.0: {} + client-only@0.0.1: {} cliui@6.0.0: @@ -11232,6 +11438,8 @@ snapshots: graphemer@1.4.0: {} + graphql@16.8.2: {} + gtoken@6.1.2: dependencies: gaxios: 5.1.3 @@ -11285,6 +11493,8 @@ snapshots: dependencies: function-bind: 1.1.2 + headers-polyfill@4.0.3: {} + hexer@1.5.0: dependencies: ansi-color: 0.2.1 @@ -11468,6 +11678,8 @@ snapshots: is-negative-zero@2.0.3: {} + is-node-process@1.2.0: {} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 @@ -11944,8 +12156,32 @@ snapshots: optionalDependencies: msgpackr-extract: 3.0.2 + msw@2.3.1(typescript@5.4.5): + dependencies: + '@bundled-es-modules/cookie': 2.0.0 + '@bundled-es-modules/statuses': 1.0.1 + '@inquirer/confirm': 3.1.9 + '@mswjs/cookies': 1.1.0 + '@mswjs/interceptors': 0.29.1 + '@open-draft/until': 2.1.0 + '@types/cookie': 0.6.0 + '@types/statuses': 2.0.5 + chalk: 4.1.2 + graphql: 16.8.2 + headers-polyfill: 4.0.3 + is-node-process: 1.2.0 + outvariant: 1.4.2 + path-to-regexp: 6.2.2 + strict-event-emitter: 0.5.1 + type-fest: 4.20.0 + yargs: 17.7.2 + optionalDependencies: + typescript: 5.4.5 + multiformats@9.9.0: {} + mute-stream@1.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -12190,6 +12426,8 @@ snapshots: outdent@0.5.0: {} + outvariant@1.4.2: {} + p-cancelable@3.0.0: {} p-filter@2.1.0: @@ -12252,6 +12490,8 @@ snapshots: path-to-regexp@0.1.7: {} + path-to-regexp@6.2.2: {} + path-type@4.0.0: {} pathe@1.1.2: {} @@ -12300,6 +12540,14 @@ snapshots: postcss: 8.4.38 ts-node: 10.9.2(@types/node@18.19.31)(typescript@5.4.5) + postcss-load-config@4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)): + dependencies: + lilconfig: 3.1.1 + yaml: 2.4.2 + optionalDependencies: + postcss: 8.4.38 + ts-node: 10.9.2(@types/node@20.14.2)(typescript@5.4.5) + postcss-nested@6.0.1(postcss@8.4.38): dependencies: postcss: 8.4.38 @@ -12811,6 +13059,8 @@ snapshots: signal-exit@3.0.7: {} + signal-exit@4.1.0: {} + simple-concat@1.0.1: {} simple-functional-loader@1.2.1: @@ -12916,6 +13166,8 @@ snapshots: optionalDependencies: bare-events: 2.2.2 + strict-event-emitter@0.5.1: {} + string-template@0.2.1: {} string-width@4.2.3: @@ -13074,6 +13326,33 @@ snapshots: transitivePeerDependencies: - ts-node + tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)): + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.5.3 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.0 + lilconfig: 2.1.0 + micromatch: 4.0.5 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.0 + postcss: 8.4.38 + postcss-import: 15.1.0(postcss@8.4.38) + postcss-js: 4.0.1(postcss@8.4.38) + postcss-load-config: 4.0.2(postcss@8.4.38)(ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5)) + postcss-nested: 6.0.1(postcss@8.4.38) + postcss-selector-parser: 6.0.13 + resolve: 1.22.8 + sucrase: 3.34.0 + transitivePeerDependencies: + - ts-node + tapable@2.2.1: {} tar-fs@2.1.1: @@ -13229,6 +13508,25 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-node@10.9.2(@types/node@20.14.2)(typescript@5.4.5): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.14.2 + acorn: 8.11.3 + acorn-walk: 8.3.2 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.4.5 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -13305,12 +13603,16 @@ snapshots: type-fest@0.20.2: {} + type-fest@0.21.3: {} + type-fest@0.6.0: {} type-fest@0.7.1: {} type-fest@0.8.1: {} + type-fest@4.20.0: {} + type-is@1.6.18: dependencies: media-typer: 0.3.0