From 599ecc8d7669f23333ac058e4e211068f276b912 Mon Sep 17 00:00:00 2001 From: NiranjanaNS Date: Fri, 9 Jan 2026 09:46:23 +0530 Subject: [PATCH 1/2] chore: backend --- backend/src/data-source.ts | 2 +- backend/src/modules/auth/auth.controller.ts | 10 +++++----- backend/src/modules/auth/auth.schema.ts | 4 ++++ backend/src/modules/health/health.ts | 6 +++--- backend/src/modules/user/user.controller.ts | 4 ++-- backend/src/server.ts | 4 ++-- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/backend/src/data-source.ts b/backend/src/data-source.ts index 5a8a7d6..44bf03a 100644 --- a/backend/src/data-source.ts +++ b/backend/src/data-source.ts @@ -5,7 +5,7 @@ import { Otp } from './entities/opt'; if (!process.env.DATABASE_URL) { throw new Error('DATABASE_URL is not defined'); } -export const appDataSouce = new DataSource({ +export const appDataSource = new DataSource({ type: 'postgres', url: process.env.DATABASE_URL, ssl: { diff --git a/backend/src/modules/auth/auth.controller.ts b/backend/src/modules/auth/auth.controller.ts index 7e3e805..e99892e 100644 --- a/backend/src/modules/auth/auth.controller.ts +++ b/backend/src/modules/auth/auth.controller.ts @@ -3,7 +3,7 @@ import { logger } from '../../utils/logger'; import { emailSchema, registerSchema } from './auth.schema'; import { sendOtpEmail } from '../../Services/email.service'; import { generateotp } from '../../utils/otp'; -import { appDataSouce } from '../../data-source'; +import { appDataSource } from '../../data-source'; import { Otp } from '../../entities/opt'; import { User } from '../../entities/User'; @@ -20,7 +20,7 @@ export const sendOtp = async ( .status(400) .json({ message: 'validation vailed', error: result.error?.format() }); } - const otpRep = appDataSouce.getRepository(Otp); + const otpRep = appDataSource.getRepository(Otp); const otpcode = generateotp(); logger.debug({ otpcode }, 'otp is'); const email = result.data?.email; @@ -49,7 +49,7 @@ export const verifyotp = async ( return res.status(400).json({ message: ' otp and email are required' }); } - const otpRepo = appDataSouce.getRepository(Otp); + const otpRepo = appDataSource.getRepository(Otp); const otpRecord = await otpRepo.findOne({ where: { email, @@ -101,8 +101,8 @@ export const register = async ( if (!otpId) { return res.status(400).json({ message: 'otpid requuired' }); } - const otpRepo = appDataSouce.getRepository(Otp); - const userRepo = appDataSouce.getRepository(User); + const otpRepo = appDataSource.getRepository(Otp); + const userRepo = appDataSource.getRepository(User); const otpRecord = await otpRepo.findOne({ where: { id: otpId }, diff --git a/backend/src/modules/auth/auth.schema.ts b/backend/src/modules/auth/auth.schema.ts index 8d80870..a12c0b1 100644 --- a/backend/src/modules/auth/auth.schema.ts +++ b/backend/src/modules/auth/auth.schema.ts @@ -3,6 +3,10 @@ export const emailSchema = z.object({ email: z.string().email('Invalid email'), }); +export const numberSchema = z.object({ + phoneNumber: z.number(), +}); + export const registerSchema = z.object({ otpId: z.string().uuid(), name: z.string().min(1, 'name is required'), diff --git a/backend/src/modules/health/health.ts b/backend/src/modules/health/health.ts index 4f62865..ff4bcac 100644 --- a/backend/src/modules/health/health.ts +++ b/backend/src/modules/health/health.ts @@ -1,16 +1,16 @@ import express from 'express'; -import { appDataSouce } from '../../data-source'; +import { appDataSource } from '../../data-source'; const Healthrouter = express.Router(); Healthrouter.get('/', async (req, res) => { try { - if (!appDataSouce.isInitialized) { + if (!appDataSource.isInitialized) { return res.status(503).json({ status: 'eroor', service: 'mysocial-code-backend', db: 'not initialized', }); } - await appDataSouce.query('SELECT 1'); + await appDataSource.query('SELECT 1'); return res.status(200).json({ status: 'ok', service: 'mysocial-code-backend', diff --git a/backend/src/modules/user/user.controller.ts b/backend/src/modules/user/user.controller.ts index 51f6b82..8876c8f 100644 --- a/backend/src/modules/user/user.controller.ts +++ b/backend/src/modules/user/user.controller.ts @@ -1,7 +1,7 @@ import { NextFunction, Request, Response } from 'express'; import { PutObjectCommand } from '@aws-sdk/client-s3'; import { r2 } from '../../utils/r2'; -import { appDataSouce } from '../../data-source'; +import { appDataSource } from '../../data-source'; import { User } from '../../entities/User'; import { logger } from '../../utils/logger'; export const uploadAvatar = async ( @@ -25,7 +25,7 @@ export const uploadAvatar = async ( }), ); const imageUrl = `${process.env.R2_ENDPOINT}/${process.env.R2_BUCKET_NAME}/${key}`; - const userRepo = appDataSouce.getRepository(User); + const userRepo = appDataSource.getRepository(User); await userRepo.update(userId, { profileImageUrl: imageUrl }); res .status(200) diff --git a/backend/src/server.ts b/backend/src/server.ts index 490bc21..c47fda8 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -2,14 +2,14 @@ import dotenv from 'dotenv'; dotenv.config(); import app from './app'; import { logger } from './utils/logger'; -import { appDataSouce } from './data-source'; +import { appDataSource } from './data-source'; if (!process.env.PORT) { throw new Error('PORT is not defined in environment variables'); } (async () => { try { - await appDataSouce.initialize(); + await appDataSource.initialize(); logger.info('database connected success fully'); } catch (error) { logger.error({ err: error }, 'error connecting the database'); From affc91f1977365dfddf141a0c26bcfddc4e496ac Mon Sep 17 00:00:00 2001 From: NiranjanaNS Date: Sat, 17 Jan 2026 14:34:14 +0530 Subject: [PATCH 2/2] chore: rabbitmq connected and initialized --- backend/package.json | 2 + backend/src/Services/authToken.ts | 4 +- backend/src/data-source.ts | 2 +- backend/src/entities/{opt.ts => otp.ts} | 0 backend/src/messaging/jobTypes.ts | 5 ++ backend/src/messaging/rabbitmq/connect.ts | 36 +++++++++ .../src/messaging/rabbitmq/consume/index.ts | 33 +++++++++ backend/src/messaging/rabbitmq/publish.ts | 17 +++++ backend/src/messaging/rabbitmq/queues.ts | 5 ++ backend/src/modules/auth/auth.controller.ts | 8 +- frontend/lib/api.ts | 2 +- pnpm-lock.yaml | 73 +++++++++++++++++-- 12 files changed, 177 insertions(+), 10 deletions(-) rename backend/src/entities/{opt.ts => otp.ts} (100%) create mode 100644 backend/src/messaging/jobTypes.ts create mode 100644 backend/src/messaging/rabbitmq/connect.ts create mode 100644 backend/src/messaging/rabbitmq/consume/index.ts create mode 100644 backend/src/messaging/rabbitmq/publish.ts create mode 100644 backend/src/messaging/rabbitmq/queues.ts diff --git a/backend/package.json b/backend/package.json index 19aaf8a..1f7c1a0 100644 --- a/backend/package.json +++ b/backend/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@aws-sdk/client-s3": "^3.962.0", + "amqplib": "^0.10.9", "bcrypt": "^6.0.0", "cors": "^2.8.5", "dotenv": "^17.2.3", @@ -28,6 +29,7 @@ "@commitlint/cli": "^20.2.0", "@commitlint/config-conventional": "^20.2.0", "@eslint/js": "^9.39.2", + "@types/amqplib": "^0.10.8", "@types/bcrypt": "^6.0.0", "@types/cors": "^2.8.19", "@types/express": "^5.0.6", diff --git a/backend/src/Services/authToken.ts b/backend/src/Services/authToken.ts index 2f1d433..466f768 100644 --- a/backend/src/Services/authToken.ts +++ b/backend/src/Services/authToken.ts @@ -1,4 +1,4 @@ -import { appDataSouce } from '../data-source'; +import { appDataSource } from '../data-source'; import { RefreshTokenEntity } from '../entities/refreshToken'; import { User } from '../entities/User'; import { generateRefreshToken, hashRefreshToken } from './refreshToken'; @@ -15,7 +15,7 @@ export const createRefreshTokenSession = async (user: User) => { expiresAt.setDate(expiresAt.getDate() + REFRESH_TOKEN_DAYS); - const repo = appDataSouce.getRepository(RefreshTokenEntity); + const repo = appDataSource.getRepository(RefreshTokenEntity); console.log('Got refresh token repository'); await repo.save({ diff --git a/backend/src/data-source.ts b/backend/src/data-source.ts index 6d372c0..9fe08ee 100644 --- a/backend/src/data-source.ts +++ b/backend/src/data-source.ts @@ -1,7 +1,7 @@ import 'dotenv/config'; import { DataSource } from 'typeorm'; import { User } from './entities/User'; -import { Otp } from './entities/opt'; +import { Otp } from './entities/otp'; import { RefreshTokenEntity } from './entities/refreshToken'; if (!process.env.DATABASE_URL) { throw new Error('DATABASE_URL is not defined'); diff --git a/backend/src/entities/opt.ts b/backend/src/entities/otp.ts similarity index 100% rename from backend/src/entities/opt.ts rename to backend/src/entities/otp.ts diff --git a/backend/src/messaging/jobTypes.ts b/backend/src/messaging/jobTypes.ts new file mode 100644 index 0000000..95d49b8 --- /dev/null +++ b/backend/src/messaging/jobTypes.ts @@ -0,0 +1,5 @@ +export interface SendOtpJob { + phone: string; + otp: string; + purpose: 'login' | 'register' | 'reset_password'; +} \ No newline at end of file diff --git a/backend/src/messaging/rabbitmq/connect.ts b/backend/src/messaging/rabbitmq/connect.ts new file mode 100644 index 0000000..375874e --- /dev/null +++ b/backend/src/messaging/rabbitmq/connect.ts @@ -0,0 +1,36 @@ +import * as amqp from "amqplib"; +import { Channel } from "amqplib"; +import { QUEUES } from "./queues"; + +const RABBITMQ_URL = + process.env.RABBITMQ_URL || "amqp://guest:guest@localhost:5672"; + +let channel: Channel | null = null; + +export const connectRabbitMQ = async (): Promise => { + if (channel) return; + + try { + console.log("🔌 Connecting to RabbitMQ..."); + + const connection = await amqp.connect(RABBITMQ_URL); + channel = await connection.createChannel(); + + // declare queues ONCE + for (const queue of Object.values(QUEUES)) { + await channel.assertQueue(queue, { durable: true }); + } + + console.log("RabbitMQ connected"); + } catch (err) { + console.error("RabbitMQ connection failed", err); + process.exit(1); + } +}; + +export const getChannel = (): Channel => { + if (!channel) { + throw new Error("RabbitMQ channel not initialized"); + } + return channel; +}; diff --git a/backend/src/messaging/rabbitmq/consume/index.ts b/backend/src/messaging/rabbitmq/consume/index.ts new file mode 100644 index 0000000..482524e --- /dev/null +++ b/backend/src/messaging/rabbitmq/consume/index.ts @@ -0,0 +1,33 @@ +import { connectRabbitMQ, getChannel } from "../connect"; +import { QUEUES } from "../queues"; +import { SendOtpJob } from "../../jobTypes"; + +const startOtpWorker = async () => { + await connectRabbitMQ(); + const channel = getChannel(); + + channel.prefetch(1); // process one OTP at a time + + console.log("OTP Worker running"); + + channel.consume(QUEUES.SEND_OTP, async (msg) => { + if (!msg) return; + + try { + const job: SendOtpJob = JSON.parse(msg.content.toString()); + + console.log(`Sending OTP ${job.otp} to ${job.phone}`); + + // Here is where Twilio / SMS provider goes + await new Promise((r) => setTimeout(r, 1500)); + + channel.ack(msg); + console.log("OTP sent"); + } catch (err) { + console.error("OTP failed", err); + channel.nack(msg, false, false); + } + }); +}; + +startOtpWorker(); diff --git a/backend/src/messaging/rabbitmq/publish.ts b/backend/src/messaging/rabbitmq/publish.ts new file mode 100644 index 0000000..e01acad --- /dev/null +++ b/backend/src/messaging/rabbitmq/publish.ts @@ -0,0 +1,17 @@ +import { getChannel } from "./connect"; +import { QUEUES, QueueKey } from "./queues"; + +export const publish = async ( + queue: QueueKey, + payload: T +): Promise => { + const channel = getChannel(); + + channel.sendToQueue( + QUEUES[queue], + Buffer.from(JSON.stringify(payload)), + { persistent: true } + ); + + console.log("📤 OTP job queued"); +}; diff --git a/backend/src/messaging/rabbitmq/queues.ts b/backend/src/messaging/rabbitmq/queues.ts new file mode 100644 index 0000000..14ec5ac --- /dev/null +++ b/backend/src/messaging/rabbitmq/queues.ts @@ -0,0 +1,5 @@ +export const QUEUES = { + SEND_OTP: "send_otp_queue", +} as const; + +export type QueueKey = keyof typeof QUEUES; diff --git a/backend/src/modules/auth/auth.controller.ts b/backend/src/modules/auth/auth.controller.ts index b3efec5..dcc30fb 100644 --- a/backend/src/modules/auth/auth.controller.ts +++ b/backend/src/modules/auth/auth.controller.ts @@ -4,11 +4,12 @@ import { registerSchema, phoneSchema, loginSchema } from './auth.schema'; import { sendOtpSms } from '../../Services/sms.service'; import { generateotp } from '../../utils/otp'; import { appDataSource } from '../../data-source'; -import { Otp } from '../../entities/opt'; +import { Otp } from '../../entities/otp'; import { User } from '../../entities/User'; import { signAccessToken } from '../../Services/jwt.service'; import { createRefreshTokenSession } from '../../Services/authToken'; import bcrypt from 'bcrypt'; +// import { publish } from '../../messaging/rabbitmq/publish'; export const sendOtp = async ( req: Request, @@ -52,6 +53,11 @@ export const sendOtp = async ( await sendOtpSms(phoneNumber, otpCode.toString()); + // await publish('SEND_OTP', { + // phone: phoneNumber, + // otp: otpCode.toString(), + // }); + await otpRepo.delete({ phoneNumber }); await otpRepo.save({ phoneNumber, diff --git a/frontend/lib/api.ts b/frontend/lib/api.ts index 7a54c2a..e63b49c 100644 --- a/frontend/lib/api.ts +++ b/frontend/lib/api.ts @@ -1,6 +1,6 @@ import axios from 'axios'; const api = axios.create({ - baseURL: 'http://10.10.1.200:4000', + baseURL: 'http://172.28.32.1:4000', timeout: 20000, headers: { 'Content-Type': 'application/json', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e81848..181ba95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@aws-sdk/client-s3': specifier: ^3.962.0 version: 3.962.0 + amqplib: + specifier: ^0.10.9 + version: 0.10.9 bcrypt: specifier: ^6.0.0 version: 6.0.0 @@ -84,6 +87,9 @@ importers: '@eslint/js': specifier: ^9.39.2 version: 9.39.2 + '@types/amqplib': + specifier: ^0.10.8 + version: 0.10.8 '@types/bcrypt': specifier: ^6.0.0 version: 6.0.0 @@ -2812,6 +2818,12 @@ packages: integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, } + '@types/amqplib@0.10.8': + resolution: + { + integrity: sha512-vtDp8Pk1wsE/AuQ8/Rgtm6KUZYqcnTgNvEHwzCkX8rL7AGsC6zqAfKAAJhUZXFhM/Pp++tbnUHiam/8vVpPztA==, + } + '@types/babel__core@7.20.5': resolution: { @@ -3456,6 +3468,13 @@ packages: integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==, } + amqplib@0.10.9: + resolution: + { + integrity: sha512-jwSftI4QjS3mizvnSnOrPGYiUnm1vI2OP1iXeOUz5pb74Ua0nbf6nPyyTzuiCLEE3fMpaJORXh2K/TQ08H5xGA==, + } + engines: { node: '>=10' } + anser@1.4.10: resolution: { @@ -3929,6 +3948,12 @@ packages: integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, } + buffer-more-ints@1.0.0: + resolution: + { + integrity: sha512-EMetuGFz5SLsT0QTnXzINh4Ksr+oo4i+UGTXEshiGCQWnsgSs7ZhJ8fzlwQ+OzEMs0MpDAMr1hxnblp5a4vcHg==, + } + buffer@5.7.1: resolution: { @@ -7703,6 +7728,12 @@ packages: } engines: { node: '>=6' } + querystringify@2.2.0: + resolution: + { + integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==, + } + queue@6.0.2: resolution: { @@ -8026,6 +8057,12 @@ packages: } engines: { node: '>= 4.0.0' } + requires-port@1.0.0: + resolution: + { + integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==, + } + resolve-from@4.0.0: resolution: { @@ -9148,6 +9185,12 @@ packages: integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==, } + url-parse@1.5.10: + resolution: + { + integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==, + } + use-callback-ref@1.3.3: resolution: { @@ -11996,6 +12039,10 @@ snapshots: tslib: 2.8.1 optional: true + '@types/amqplib@0.10.8': + dependencies: + '@types/node': 25.0.3 + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.28.5 @@ -12433,6 +12480,11 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + amqplib@0.10.9: + dependencies: + buffer-more-ints: 1.0.0 + url-parse: 1.5.10 + anser@1.4.10: {} ansi-escapes@4.3.2: @@ -12782,6 +12834,8 @@ snapshots: buffer-from@1.1.2: {} + buffer-more-ints@1.0.0: {} + buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -13326,7 +13380,7 @@ snapshots: '@typescript-eslint/eslint-plugin': 8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-expo: 1.0.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@2.6.1)) @@ -13346,7 +13400,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.3 @@ -13361,14 +13415,14 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@2.6.1)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) transitivePeerDependencies: - supports-color @@ -13392,7 +13446,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2(jiti@2.6.1) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@2.6.1)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)))(eslint@9.39.2(jiti@2.6.1)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -15229,6 +15283,8 @@ snapshots: split-on-first: 1.1.0 strict-uri-encode: 2.0.0 + querystringify@2.2.0: {} + queue@6.0.2: dependencies: inherits: 2.0.4 @@ -15498,6 +15554,8 @@ snapshots: rc: 1.2.8 resolve: 1.7.1 + requires-port@1.0.0: {} + resolve-from@4.0.0: {} resolve-from@5.0.0: {} @@ -16208,6 +16266,11 @@ snapshots: url-join@4.0.1: {} + url-parse@1.5.10: + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + use-callback-ref@1.3.3(@types/react@19.1.17)(react@19.1.0): dependencies: react: 19.1.0