diff --git a/.env.demo b/.env.demo index 7c5a134e15..67e794f546 100644 --- a/.env.demo +++ b/.env.demo @@ -26,6 +26,9 @@ NATS_HOST=your-ip NATS_PORT=4222 NATS_URL=nats://your-ip:4222 +NATS_PASSWORD=xxxx +NATS_USER=xxxx + REDIS_HOST=your-ip REDIS_PORT=6379 @@ -230,3 +233,16 @@ PRISMA_LOGS = error # DB_ALERT_ENABLE= # DB_ALERT_EMAILS= + + + +CLIENT_EMAIL= +PRIVATE_KEY= +PROJECT_ID= + +CONSUMER_CONFIG_ACK_WAIT=10_000 # IN nanos(10_000) +CONSUMER_CONFIG_MAX_DELIVER=4 + +AGGREGATE_STREAM=aggregate +DID_STREAM=did-notify +PULL_CONSUMER=hub-pull-consumer \ No newline at end of file diff --git a/.env.sample b/.env.sample index 7cc3215986..386ddadf0b 100644 --- a/.env.sample +++ b/.env.sample @@ -59,6 +59,9 @@ NATS_HOST='0.0.0.0' NATS_PORT=4222 NATS_URL=nats://0.0.0.0:4222 +NATS_PASSWORD=xxxx +NATS_USER=xxxx + REDIS_HOST='0.0.0.0' REDIS_PORT=6379 @@ -259,3 +262,15 @@ RESEND_API_KEY=re_xxxxxxxxxx # DB_ALERT_EMAILS= # Boolean: to enable/disable db alerts. This needs the 'utility' microservice # DB_ALERT_ENABLE= + + +CLIENT_EMAIL= +PRIVATE_KEY= +PROJECT_ID= + +CONSUMER_CONFIG_ACK_WAIT=10_000 # IN nanos(10_000) +CONSUMER_CONFIG_MAX_DELIVER=4 + +AGGREGATE_STREAM=aggregate +DID_STREAM=did-notify +PULL_CONSUMER=hub-pull-consumer \ No newline at end of file diff --git a/apps/api-gateway/src/authz/guards/client-access-guard.ts b/apps/api-gateway/src/authz/guards/client-access-guard.ts new file mode 100644 index 0000000000..cac92acdd0 --- /dev/null +++ b/apps/api-gateway/src/authz/guards/client-access-guard.ts @@ -0,0 +1,17 @@ +import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +@Injectable() +export class ClientAccessGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + + const { user } = request; + + if (!user || !Object.prototype.hasOwnProperty.call(user, 'client_id')) { + throw new UnauthorizedException('You do not have access'); + } + + return true; + } +} diff --git a/apps/api-gateway/src/main.ts b/apps/api-gateway/src/main.ts index 8fb86c4653..0c4dcef47f 100644 --- a/apps/api-gateway/src/main.ts +++ b/apps/api-gateway/src/main.ts @@ -125,6 +125,7 @@ async function bootstrap(): Promise { xssFilter: true }) ); + Logger.log('API-Gateway is listening to NATS', 'NATS-CONNECTION'); await app.listen(process.env.API_GATEWAY_PORT, `${process.env.API_GATEWAY_HOST}`); Logger.log(`API Gateway is listening on port ${process.env.API_GATEWAY_PORT}`, 'Success'); diff --git a/apps/api-gateway/src/notification/notification.controller.ts b/apps/api-gateway/src/notification/notification.controller.ts index e962d2e3fa..140c185040 100644 --- a/apps/api-gateway/src/notification/notification.controller.ts +++ b/apps/api-gateway/src/notification/notification.controller.ts @@ -1,6 +1,7 @@ import { CustomExceptionFilter } from '@credebl/common/exception-handler'; -import { Body, Controller, HttpStatus, Logger, Post, Res, UseFilters } from '@nestjs/common'; +import { Body, Controller, HttpStatus, Logger, Post, Res, UseFilters, UseGuards } from '@nestjs/common'; import { + ApiBearerAuth, ApiExcludeEndpoint, ApiForbiddenResponse, ApiOperation, @@ -20,6 +21,8 @@ import { IResponse } from '@credebl/common/interfaces/response.interface'; import { Response } from 'express'; import { ResponseMessages } from '@credebl/common/response-messages'; import { NotificationService } from './notification.service'; +import { ClientAccessGuard } from '../authz/guards/client-access-guard'; +import { AuthGuard } from '@nestjs/passport'; @Controller('notification') @UseFilters(CustomExceptionFilter) @@ -93,6 +96,8 @@ export class NotificationController { */ @Post('/register/holder-notification') // @ApiExcludeEndpoint() + @ApiBearerAuth() + @UseGuards(AuthGuard('jwt'), ClientAccessGuard) @ApiOperation({ summary: `Register holder for notification`, description: `Register holder for notification` diff --git a/apps/notification/package.json b/apps/notification/package.json new file mode 100644 index 0000000000..ec5ff9b375 --- /dev/null +++ b/apps/notification/package.json @@ -0,0 +1,9 @@ +{ + "name": "@credebl/notification", + "version": "1.0.0", + "private": true, + "type": "module", + "dependencies": { + "firebase-admin": "^13.6.0" + } +} \ No newline at end of file diff --git a/apps/notification/src/nats/interfaces.ts b/apps/notification/src/nats/interfaces.ts new file mode 100644 index 0000000000..a528a4f03d --- /dev/null +++ b/apps/notification/src/nats/interfaces.ts @@ -0,0 +1,14 @@ +export interface INatsEventMessage { + event: string; + sessionId: string; + timestamp: string; + senderCode: string; +} + +export interface INatsUserRequestData { + did: string; + sessions: { + sessionId: string; + orgCode: string; + }[]; +} diff --git a/apps/notification/src/nats/jetstream.consumer.ts b/apps/notification/src/nats/jetstream.consumer.ts new file mode 100644 index 0000000000..3bf3c18487 --- /dev/null +++ b/apps/notification/src/nats/jetstream.consumer.ts @@ -0,0 +1,164 @@ +import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; +import { Consumer } from 'nats'; +import { NatsService } from './nats.service'; +import { + DID_STREAM, + ensureConsumer, + ensureDidStream, + publishToJetStream, + PULL_CONSUMER, + AGGREGATE_STREAM +} from './jetstream.setup'; +import { PendingAckStore } from './pendingAckStore'; +import { HolderNotificationRepository } from '../holder-notification.repository'; +import { Message } from 'firebase-admin/lib/messaging/messaging-api'; +import * as admin from 'firebase-admin'; +import { IHolderNotification } from '@credebl/common/interfaces/holder-notification.interfaces'; + +const EVENT_PRESENTATION_ACK = 'presentation.ack'; +const EVENT_PRESENTATION_PURGED = 'presentation.purged'; +@Injectable() +export class JetStreamConsumer implements OnApplicationBootstrap { + constructor( + private readonly nats: NatsService, + private readonly logger: Logger, + private readonly pendingAckStore: PendingAckStore, + private readonly holderNotificationRepository: HolderNotificationRepository + ) {} + + async onApplicationBootstrap(): Promise { + if (!admin.apps.length) { + const projectId = process.env.PROJECT_ID; + const clientEmail = process.env.CLIENT_EMAIL; + const privateKey = process.env.PRIVATE_KEY; + + if (!projectId || !clientEmail || !privateKey) { + throw new Error('Missing Firebase credentials: PROJECT_ID, CLIENT_EMAIL, and PRIVATE_KEY are required'); + } + admin.initializeApp({ + credential: admin.credential.cert({ + projectId, + clientEmail, + privateKey: privateKey.replace(/\\n/g, '\n') + }) + }); + } + + const js = this.nats.jetstream(); // ✅ now safe + + const jsm = this.nats.jetstreamManager(); + await ensureConsumer(jsm); + const consumer: Consumer = await js.consumers.get(AGGREGATE_STREAM, PULL_CONSUMER); + + this.logger.log('[NATS] JetStream consumer started'); + + this.consume(consumer).catch((err) => { + this.logger.error('[NATS] Consumer crashed', err); + }); + } + + private async consume(consumer: Consumer): Promise { + this.logger.log(`[NATS] Starting to consume messages from consumer ${consumer.info}`); + for await (const msg of await consumer.consume()) { + try { + const { subject } = msg; + this.logger.log(`[NATS] Message subject: ${subject}`); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [_, domain, event, orgCode, sessionId] = subject.split('.'); + const consumerName = `notify-session-${sessionId}`; + + this.logger.log({ + domain, + event, + orgCode, + sessionId + }); + + const payload = msg.json(); + const notificationDetail = await this.holderNotificationRepository.getHolderNotificationBySessionId(sessionId); + this.logger.debug(`[NATS] Message received ${JSON.stringify(payload)}`); + this.logger.log(`[NATS] Processing message, ${JSON.stringify({ deliveryCount: msg.info.deliveryCount })}`); + const maxDeliver = process.env.CONSUMER_CONFIG_MAX_DELIVER + ? Number(process.env.CONSUMER_CONFIG_MAX_DELIVER) + : 4; + if (maxDeliver - 1 < msg.info.deliveryCount) { + //------------ Moving message to DID stream ---------------// + await this.moveMsgToDidStream(notificationDetail, msg); + + //------------- Sending Push Notification via FCM ----------------// + this.sendNotificationToHolder(event, orgCode, notificationDetail); + msg.ack(); + } else { + // ------------- Publishing Messages via NATS ----------------// + + const ackKey = this.pendingAckStore.save(AGGREGATE_STREAM, consumerName, msg); + + this.logger.log(`[NATS] Notification detail fetched for session ID: ${sessionId}`); + if (!notificationDetail) { + this.logger.error(`[NATS] No notification detail found for session ID: ${sessionId}`); + msg.nak(); + continue; + } + await this.nats.publish(`${notificationDetail.holderDid}`, { + payload, + ackKey, + subject: msg.subject, + event: `${domain}.${event}` + }); + + this.logger.log(`[NATS] Message published to ${notificationDetail.holderDid} for session ${sessionId}`); + } + + // business logic + // msg.ack(); + } catch (err) { + this.logger.error('[NATS] Processing failed', err); + msg.nak(); + } + } + } + + private sendNotificationToHolder(event: string, orgCode: string, notificationDetail: IHolderNotification): void { + let notificationTitle = ''; + if (EVENT_PRESENTATION_ACK === event) { + notificationTitle = `Your data delivered to ${orgCode}`; + } else if (EVENT_PRESENTATION_PURGED === event) { + notificationTitle = `Your data purged from ${orgCode}`; + } + this.logger.log('Now push notifications will be sent to user'); + const notificationPayload: Message = { + notification: { + title: notificationTitle, + body: `Open an app to view more details` + }, + token: notificationDetail.fcmToken + }; + // Send the notification to the specified device + admin + .messaging() + .send(notificationPayload) + .then((response) => { + this.logger.log('Successfully sent message:', response); + }) + .catch((error) => { + this.logger.error('Error sending message:', error); + }); + } + + private async moveMsgToDidStream(notificationDetail: IHolderNotification, msg): Promise { + this.logger.log('[NATS] Moving message to DID stream for FCM notification'); + //const notifyStream = process.env.STREAM_NOTIFY ?? "notify"; + const subjectName = `${DID_STREAM}.${notificationDetail.holderDid}`; + const jsm = this.nats.jetstreamManager(); + const jsc = this.nats.jetstream(); + await ensureDidStream(jsm); + + const decoder = new TextDecoder(); // or 'UTF-8', 'windows-1251', etc. + const msgData = decoder.decode(msg.data); + + //await ensureStreamExists(streamName); + // Publish to JetStream for guaranteed delivery + const jsAck = await publishToJetStream(subjectName, msgData, jsc); + this.logger.log(`[NATS] Message moved to DID stream: ${JSON.stringify(jsAck)}`); + } +} diff --git a/apps/notification/src/nats/jetstream.setup.ts b/apps/notification/src/nats/jetstream.setup.ts new file mode 100644 index 0000000000..8df06230f1 --- /dev/null +++ b/apps/notification/src/nats/jetstream.setup.ts @@ -0,0 +1,123 @@ +/* eslint-disable camelcase */ +import { Logger } from '@nestjs/common'; +import { AckPolicy, DeliverPolicy, JetStreamClient, JetStreamManager, RetentionPolicy, StorageType, nanos } from 'nats'; +import assert = require('node:assert'); + +export const AGGREGATE_STREAM = process.env.AGGREGATE_STREAM || 'aggregate'; +export const DID_STREAM = process.env.DID_STREAM || 'did-notify'; +export const PULL_CONSUMER = process.env.PULL_CONSUMER || 'hub-pull-consumer'; +const logger = new Logger('JetStreamSetup'); +/** + * Ensures the JetStream stream exists + */ +export async function ensureStream(jsm: JetStreamManager): Promise { + try { + await jsm.streams.info(AGGREGATE_STREAM); + logger.log(`[NATS] Stream '${AGGREGATE_STREAM}' already exists`); + } catch (error) { + logger.error( + `[NATS] Error checking stream '${AGGREGATE_STREAM}': ${error instanceof Error ? error.message : 'Unknown error'}` + ); + logger.log(`[NATS] Creating stream '${AGGREGATE_STREAM}'`); + await jsm.streams.add({ + name: AGGREGATE_STREAM, + //subjects: SUBJECTS, + retention: RetentionPolicy.Workqueue, + storage: StorageType.File, + sources: [] + }); + } +} + +export async function ensureDidStream(jsm: JetStreamManager): Promise { + try { + await jsm.streams.info(DID_STREAM); + logger.log(`[NATS] Stream '${DID_STREAM}' already exists`); + } catch (error) { + logger.error( + `[NATS] Error checking stream '${DID_STREAM}': ${error instanceof Error ? error.message : 'Unknown error'}` + ); + logger.log(`[NATS] Creating stream '${DID_STREAM}'`); + await jsm.streams.add({ + name: DID_STREAM, + subjects: [`${DID_STREAM}.>`], + retention: RetentionPolicy.Workqueue, + storage: StorageType.File + }); + } +} + +/** + * Ensures the JetStream consumer exists + */ +export async function ensureConsumer(jsm: JetStreamManager): Promise { + try { + await jsm.consumers.info(AGGREGATE_STREAM, PULL_CONSUMER); + logger.log(`[NATS] Consumer '${PULL_CONSUMER}' already exists`); + } catch (error) { + logger.error( + `[NATS] Error checking consumer '${PULL_CONSUMER}': ${error instanceof Error ? error.message : 'Unknown error'}` + ); + logger.log(`[NATS] Creating consumer '${PULL_CONSUMER}'`); + await jsm.consumers.add(AGGREGATE_STREAM, { + name: PULL_CONSUMER, + durable_name: PULL_CONSUMER, + ack_policy: AckPolicy.Explicit, + deliver_policy: DeliverPolicy.All, + ack_wait: process.env.CONSUMER_CONFIG_ACK_WAIT + ? nanos(Number(process.env.CONSUMER_CONFIG_ACK_WAIT)) + : nanos(10_000), + max_deliver: process.env.CONSUMER_CONFIG_MAX_DELIVER ? Number(process.env.CONSUMER_CONFIG_MAX_DELIVER) : 4 + }); + } +} + +export function assertDefined(v: T, msg = 'Value must be defined'): asserts v is NonNullable { + assert(null !== v && v !== undefined, msg); +} + +/** + * Publish to JetStream with guaranteed delivery + * Returns acknowledgment from JetStream with stream name and sequence number + */ +export async function publishToJetStream( + subject: string, + payload: unknown, + jsc: JetStreamClient + // options?: JetStreamPublishOptions, +): Promise<{ stream: string; seq: number }> { + assertDefined(subject, 'Subject is required for publishing'); + assertDefined(payload, 'payload is required for publishing'); + + try { + // const js = await getJetStreamClient(); + logger.log(`[NATS] Publishing to JetStream: ${subject}`); + logger.debug(`[NATS] Payload for ${subject}:`, { payload }); // Only in debug mode + const ack = await jsc.publish( + `${subject}`, + // sc.encode(JSON.stringify(payload)) + JSON.stringify(payload) + ); + + logger.log( + `[NATS] Message published to JetStream - ${JSON.stringify({ + subject, + stream: ack.stream, + sequence: ack.seq, + duplicate: ack.duplicate || false + })}` + ); + + return { + stream: ack.stream, + seq: ack.seq + }; + } catch (error) { + logger.error(`[NATS] Error publishing to JetStream: ${JSON.stringify(error)}`); + logger.error('[NATS] JetStream publish failed', { + subject, + error: error instanceof Error ? error.message : 'Unknown error' + }); + throw error; + } +} diff --git a/apps/notification/src/nats/nats-subscriber.ts b/apps/notification/src/nats/nats-subscriber.ts new file mode 100644 index 0000000000..9c5c88ff3b --- /dev/null +++ b/apps/notification/src/nats/nats-subscriber.ts @@ -0,0 +1,76 @@ +import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; +import { NatsService } from './nats.service'; +import { PendingAckStore } from './pendingAckStore'; + +const sd = new TextDecoder(); +export const EVENT_USER_ACK = 'user.ack'; +@Injectable() +export class NotificationNATSSubsciber implements OnApplicationBootstrap { + constructor( + private readonly nats: NatsService, + private readonly logger: Logger, + private readonly pendingAckStore: PendingAckStore + ) {} + + async onApplicationBootstrap(): Promise { + await this.subscribe(EVENT_USER_ACK, async (data) => { + this.logger.log('[NATS] user.ack Subscriber received data:', data); + await this.handleUserAck(data as { ackKey: string }); + }); + } + + private async subscribe(subject: string, handler: (data: unknown) => void | Promise): Promise { + try { + this.logger.log(`[NATS] Setting up subscription ${JSON.stringify(subject)}`); + const nc = await this.nats.connect(); + const subscription = nc.subscribe(subject); + + // eslint-disable-next-line @typescript-eslint/explicit-function-return-type + (async () => { + for await (const msg of subscription) { + try { + const rawData = sd.decode(msg.data); + const data = JSON.parse(rawData); + await handler(data); + } catch (error) { + this.logger.error( + `[NATS] Error processing subscribed message ${JSON.stringify({ + subject, + error: error instanceof Error ? error.message : 'Unknown error' + })}` + ); + } + } + })(); + + this.logger.log('[NATS] Subscription active', { + subject + }); + } catch (error) { + this.logger.error( + `[NATS] Failed to set up subscription ${JSON.stringify({ + subject, + error: error instanceof Error ? error.message : 'Unknown error' + })}` + ); + throw error; + } + } + + async handleUserAck(data: { ackKey: string }): Promise { + const msg = this.pendingAckStore.get(data.ackKey); + + if (!msg) { + this.logger.warn('[NATS] ACK received but message not found or already acked', data); + return; + } + + try { + msg.ack(); + this.pendingAckStore.delete(data.ackKey); + this.logger.log('[NATS] Message ACKed via user confirmation'); + } catch (err) { + this.logger.error(`[NATS] Failed to ACK message ${JSON.stringify(err)}`); + } + } +} diff --git a/apps/notification/src/nats/nats.module.ts b/apps/notification/src/nats/nats.module.ts new file mode 100644 index 0000000000..46dbf209f0 --- /dev/null +++ b/apps/notification/src/nats/nats.module.ts @@ -0,0 +1,52 @@ +import { Logger, Module, OnModuleInit } from '@nestjs/common'; +import { NatsService } from './nats.service'; +import { JetStreamConsumer } from './jetstream.consumer'; +import { ensureStream } from './jetstream.setup'; +import { NotificationNATSSubsciber } from './nats-subscriber'; +import { ConfigModule } from '@nestjs/config'; +import { CommonModule } from '@credebl/common'; +import { GlobalConfigModule } from '@credebl/config'; +import { ConfigModule as PlatformConfig } from '@credebl/config/config.module'; +import { LoggerModule } from '@credebl/logger'; +import { ContextInterceptorModule } from '@credebl/context'; +import { PendingAckStore } from './pendingAckStore'; +import { HolderNotificationRepository } from '../holder-notification.repository'; +import { PrismaService } from '@credebl/prisma-service'; + +@Module({ + imports: [ + ConfigModule.forRoot(), + + CommonModule, + GlobalConfigModule, + LoggerModule, + PlatformConfig, + ContextInterceptorModule + ], + providers: [ + NatsService, + JetStreamConsumer, + NotificationNATSSubsciber, + Logger, + PendingAckStore, + HolderNotificationRepository, + PrismaService + ], + exports: [NatsService] +}) +export class NatsModule implements OnModuleInit { + constructor( + private readonly nats: NatsService, + private readonly logger: Logger + ) {} + + async onModuleInit(): Promise { + this.logger.log('[NATS] Module initializing...'); + await this.nats.connect(); + + const jsm = this.nats.jetstreamManager(); + + await ensureStream(jsm); + // await ensureConsumer(jsm); + } +} diff --git a/apps/notification/src/nats/nats.service.ts b/apps/notification/src/nats/nats.service.ts new file mode 100644 index 0000000000..61d77bffc2 --- /dev/null +++ b/apps/notification/src/nats/nats.service.ts @@ -0,0 +1,63 @@ +import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; +import { connect, JetStreamClient, JetStreamManager, NatsConnection, usernamePasswordAuthenticator } from 'nats'; + +@Injectable() +export class NatsService implements OnModuleDestroy { + public nc!: NatsConnection; + private js!: JetStreamClient; + private jsm!: JetStreamManager; + private connected = false; + + constructor(private readonly logger: Logger) {} + + async connect(): Promise { + if (this.connected) { + return this.nc; + } + + this.logger.log('[NATS] starting connection...'); + + const { NATS_URL, NATS_USER, NATS_PASSWORD } = process.env; + if (!NATS_URL || !NATS_USER || !NATS_PASSWORD) { + throw new Error('Missing NATS connection env vars (NATS_URL, NATS_USER, NATS_PASSWORD)'); + } + + this.nc = await connect({ + servers: `${process.env.NATS_URL}`.split(','), + authenticator: usernamePasswordAuthenticator(`${process.env.NATS_USER}`, `${process.env.NATS_PASSWORD}`) + }); + this.js = this.nc.jetstream(); + this.jsm = await this.nc.jetstreamManager(); + + this.connected = true; + this.logger.log('[NATS] connection established'); + return this.nc; + } + + jetstream(): JetStreamClient { + if (!this.connected) { + throw new Error('NATS not connected yet'); + } + return this.js; + } + + jetstreamManager(): JetStreamManager { + if (!this.connected) { + throw new Error('NATS not connected yet'); + } + return this.jsm; + } + + async onModuleDestroy(): Promise { + if (this.nc) { + await this.nc.drain(); + } + } + + async publish(subject: string, payload: unknown): Promise { + if (!this.connected) { + throw new Error('NATS not connected yet'); + } + this.nc.publish(subject, Buffer.from(JSON.stringify(payload))); + } +} diff --git a/apps/notification/src/nats/pendingAckStore.ts b/apps/notification/src/nats/pendingAckStore.ts new file mode 100644 index 0000000000..75e68caec8 --- /dev/null +++ b/apps/notification/src/nats/pendingAckStore.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import { JsMsg } from 'nats'; + +type PendingKey = string; + +interface PendingMessage { + msg: JsMsg; + receivedAt: number; +} + +@Injectable() +export class PendingAckStore { + private readonly store = new Map(); + + makeKey(stream: string, consumer: string, streamSequence: number): PendingKey { + return `${stream}:${consumer}:${streamSequence}`; + } + + save(stream: string, consumer: string, msg: JsMsg): PendingKey { + const { info } = msg; + if (!info) { + throw new Error('Cannot save message without info metadata'); + } + const key = this.makeKey(stream, consumer, info.streamSequence); + + this.store.set(key, { + msg, + receivedAt: Date.now() + }); + + return key; + } + + get(key: PendingKey): JsMsg | undefined { + return this.store.get(key)?.msg; + } + + delete(key: PendingKey): void { + this.store.delete(key); + } +} diff --git a/apps/notification/src/notification.module.ts b/apps/notification/src/notification.module.ts index 8da1884a1f..97bc31fc09 100644 --- a/apps/notification/src/notification.module.ts +++ b/apps/notification/src/notification.module.ts @@ -14,6 +14,7 @@ import { ConfigModule as PlatformConfig } from '@credebl/config/config.module'; import { LoggerModule } from '@credebl/logger/logger.module'; import { ContextInterceptorModule } from '@credebl/context/contextInterceptorModule'; import { HolderNotificationRepository } from './holder-notification.repository'; +import { NatsModule } from './nats/nats.module'; @Module({ imports: [ @@ -30,7 +31,8 @@ import { HolderNotificationRepository } from './holder-notification.repository'; LoggerModule, PlatformConfig, ContextInterceptorModule, - CacheModule.register({ host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }) + CacheModule.register({ host: process.env.REDIS_HOST, port: process.env.REDIS_PORT }), + NatsModule ], controllers: [NotificationController], providers: [NotificationService, NotificationRepository, HolderNotificationRepository, PrismaService, Logger] diff --git a/apps/notification/src/notification.service.ts b/apps/notification/src/notification.service.ts index dd44defae4..973de03468 100644 --- a/apps/notification/src/notification.service.ts +++ b/apps/notification/src/notification.service.ts @@ -4,12 +4,12 @@ import { RpcException } from '@nestjs/microservices'; import { NotificationRepository } from './notification.repository'; import { ResponseMessages } from '@credebl/common/response-messages'; import { CommonService } from '@credebl/common'; +import { HolderNotificationRepository } from './holder-notification.repository'; +import { NotificationStatus } from '@credebl/enum/enum'; import { ICreateHolderNotification, IHolderNotification -} from '../../../libs/common/src/interfaces/holder-notification.interfaces'; -import { HolderNotificationRepository } from './holder-notification.repository'; -import { NotificationStatus } from '@credebl/enum/enum'; +} from '@credebl/common/interfaces/holder-notification.interfaces'; @Injectable() export class NotificationService { diff --git a/apps/oid4vc-verification/src/oid4vc-verification.service.ts b/apps/oid4vc-verification/src/oid4vc-verification.service.ts index ff6a83df4c..1884abb7fe 100644 --- a/apps/oid4vc-verification/src/oid4vc-verification.service.ts +++ b/apps/oid4vc-verification/src/oid4vc-verification.service.ts @@ -322,7 +322,7 @@ export class Oid4vpVerificationService extends BaseService { // Fetch intent template using utilities service this.logger.debug( - `[createVerificationPresentation] fetching intent template for intent=${intent}, orgId=${orgId}` + `[createIntentBasedVerificationPresentation] fetching intent template for intent=${intent}, orgId=${orgId}` ); const templateData = await this.natsClient.sendNatsMessage( this.oid4vpVerificationServiceProxy, @@ -335,7 +335,7 @@ export class Oid4vpVerificationService extends BaseService { } this.logger.debug( - `[createVerificationPresentation] template fetched successfully: ${JSON.stringify(templateData)}` + `[createIntentBasedVerificationPresentation] template fetched successfully: ${JSON.stringify(templateData)}` ); // Build session request using fetched template @@ -393,7 +393,7 @@ export class Oid4vpVerificationService extends BaseService { return createdSession; } catch (error) { this.logger.error( - `[createVerificationPresentation] - error creating verification presentation: ${JSON.stringify(error?.response ?? error)}` + `[createIntentBasedVerificationPresentation] - error creating verification presentation: ${JSON.stringify(error)}` ); throw new RpcException(error?.response ?? error); } @@ -451,7 +451,7 @@ export class Oid4vpVerificationService extends BaseService { this.logger.debug(`[getVerificationSessionResponse] response fetched successfully for orgId=${orgId}`); return verifiers; } catch (error) { - this.logger.error(`[getVerificationSessionResponse] - error: ${JSON.stringify(error?.response ?? error)}`); + this.logger.error(`[getVerificationSessionResponse] - error: ${JSON.stringify(error)}`); throw new RpcException(error?.response ?? error); } } diff --git a/libs/service/nats.options.ts b/libs/service/nats.options.ts index 6051ec0f36..83918da001 100644 --- a/libs/service/nats.options.ts +++ b/libs/service/nats.options.ts @@ -5,14 +5,22 @@ export const commonNatsOptions = (name: string, isClient = true) => { const common: NatsOptions = { transport: Transport.NATS, options: { - url: `nats://${process.env.NATS_HOST}:${process.env.NATS_PORT}`, + servers: (() => { + const raw = process.env.NATS_URL ?? ''; + const servers = raw + .split(',') + .map((s) => s.trim()) + .filter(Boolean); + if (0 === servers.length) { + throw new Error('NATS_URL is required and must contain at least one server'); + } + return servers; + })(), name, maxReconnectAttempts: -1, reconnectTimeWait: 3000 } }; - const result = isClient - ? { ...common, options: { ...common.options, reconnect: true } } - : common; + const result = isClient ? { ...common, options: { ...common.options, reconnect: true } } : common; return result; }; diff --git a/package.json b/package.json index 72cc0bfcf6..5d16bf99c2 100644 --- a/package.json +++ b/package.json @@ -78,6 +78,7 @@ "dotenv": "^16.0.3", "express": "^4.18.2", "express-useragent": "^1.0.15", + "firebase-admin": "^13.6.0", "fs": "0.0.1-security", "generate-password": "^1.7.0", "helmet": "^7.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e01c31978..0a0bbec1ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -233,6 +233,9 @@ importers: express-useragent: specifier: ^1.0.15 version: 1.0.15 + firebase-admin: + specifier: ^13.6.0 + version: 13.6.0 fs: specifier: 0.0.1-security version: 0.0.1-security @@ -469,6 +472,12 @@ importers: specifier: ^5.1.6 version: 5.9.2 + apps/notification: + dependencies: + firebase-admin: + specifier: ^13.6.0 + version: 13.6.0 + libs/aws: dependencies: '@nestjs/common': @@ -908,10 +917,70 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@fastify/busboy@3.2.0': + resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} + + '@firebase/app-check-interop-types@0.3.3': + resolution: {integrity: sha512-gAlxfPLT2j8bTI/qfe3ahl2I2YcBQ8cFIBdhAQA4I2f3TndcO+22YizyGYuttLHPQEpWkhmpFW60VCFEPg4g5A==} + + '@firebase/app-types@0.9.3': + resolution: {integrity: sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==} + + '@firebase/auth-interop-types@0.2.4': + resolution: {integrity: sha512-JPgcXKCuO+CWqGDnigBtvo09HeBs5u/Ktc2GaFj2m01hLarbxthLNm7Fk8iOP1aqAtXV+fnnGj7U28xmk7IwVA==} + + '@firebase/component@0.7.0': + resolution: {integrity: sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==} + engines: {node: '>=20.0.0'} + + '@firebase/database-compat@2.1.0': + resolution: {integrity: sha512-8nYc43RqxScsePVd1qe1xxvWNf0OBnbwHxmXJ7MHSuuTVYFO3eLyLW3PiCKJ9fHnmIz4p4LbieXwz+qtr9PZDg==} + engines: {node: '>=20.0.0'} + + '@firebase/database-types@1.0.16': + resolution: {integrity: sha512-xkQLQfU5De7+SPhEGAXFBnDryUWhhlFXelEg2YeZOQMCdoe7dL64DDAd77SQsR+6uoXIZY5MB4y/inCs4GTfcw==} + + '@firebase/database@1.1.0': + resolution: {integrity: sha512-gM6MJFae3pTyNLoc9VcJNuaUDej0ctdjn3cVtILo3D5lpp0dmUHHLFN/pUKe7ImyeB1KAvRlEYxvIHNF04Filg==} + engines: {node: '>=20.0.0'} + + '@firebase/logger@0.5.0': + resolution: {integrity: sha512-cGskaAvkrnh42b3BA3doDWeBmuHFO/Mx5A83rbRDYakPjO9bJtRL3dX7javzc2Rr/JHZf4HlterTW2lUkfeN4g==} + engines: {node: '>=20.0.0'} + + '@firebase/util@1.13.0': + resolution: {integrity: sha512-0AZUyYUfpMNcztR5l09izHwXkZpghLgCUaAGjtMwXnCg3bj4ml5VgiwqOMOxJ+Nw4qN/zJAaOQBcJ7KGkWStqQ==} + engines: {node: '>=20.0.0'} + + '@google-cloud/firestore@7.11.6': + resolution: {integrity: sha512-EW/O8ktzwLfyWBOsNuhRoMi8lrC3clHM5LVFhGvO1HCsLozCOOXRAlHrYBoE6HL42Sc8yYMuCb2XqcnJ4OOEpw==} + engines: {node: '>=14.0.0'} + + '@google-cloud/paginator@5.0.2': + resolution: {integrity: sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==} + engines: {node: '>=14.0.0'} + + '@google-cloud/projectify@4.0.0': + resolution: {integrity: sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==} + engines: {node: '>=14.0.0'} + + '@google-cloud/promisify@4.0.0': + resolution: {integrity: sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==} + engines: {node: '>=14'} + + '@google-cloud/storage@7.18.0': + resolution: {integrity: sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ==} + engines: {node: '>=14'} + '@grpc/grpc-js@1.14.0': resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} engines: {node: '>=12.10.0'} + '@grpc/proto-loader@0.7.15': + resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==} + engines: {node: '>=6'} + hasBin: true + '@grpc/proto-loader@0.8.0': resolution: {integrity: sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==} engines: {node: '>=6'} @@ -1993,6 +2062,10 @@ packages: '@tokenizer/token@0.3.0': resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} @@ -2026,6 +2099,9 @@ packages: '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} + '@types/caseless@0.12.5': + resolution: {integrity: sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -2086,6 +2162,9 @@ packages: '@types/jsonwebtoken@9.0.7': resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==} + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + '@types/luxon@3.7.1': resolution: {integrity: sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg==} @@ -2131,6 +2210,9 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + '@types/request@2.48.13': + resolution: {integrity: sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==} + '@types/retry@0.12.5': resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==} @@ -2152,6 +2234,9 @@ packages: '@types/supertest@2.0.16': resolution: {integrity: sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==} + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + '@types/triple-beam@1.3.5': resolution: {integrity: sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==} @@ -2316,6 +2401,10 @@ packages: abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -2504,6 +2593,10 @@ packages: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} @@ -2641,6 +2734,9 @@ packages: resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} engines: {node: '>=0.6'} + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + binary-search@1.3.6: resolution: {integrity: sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==} @@ -3252,6 +3348,9 @@ packages: duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} + duplexify@4.1.3: + resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} + eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} @@ -3539,6 +3638,10 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -3597,6 +3700,10 @@ packages: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} + farmhash-modern@1.1.0: + resolution: {integrity: sha512-6ypT4XfgqJk/F3Yuv4SX26I3doUjt0GTG4a+JgWxXQpxXzTBq8fPUeGHfcYMMDPHJHm3yPOSjaeBwBGAHWXCdA==} + engines: {node: '>=18.0.0'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -3632,6 +3739,10 @@ packages: fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + fast-xml-parser@4.5.3: + resolution: {integrity: sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==} + hasBin: true + fast-xml-parser@5.2.5: resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} hasBin: true @@ -3639,6 +3750,10 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -3682,6 +3797,10 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} + firebase-admin@13.6.0: + resolution: {integrity: sha512-GdPA/t0+Cq8p1JnjFRBmxRxAGvF/kl2yfdhALl38PrRp325YxyQ5aNaHui0XmaKcKiGRFIJ/EgBNWFoDP0onjw==} + engines: {node: '>=18'} + flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -3729,6 +3848,10 @@ packages: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} engines: {node: '>= 0.12'} + form-data@2.5.5: + resolution: {integrity: sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==} + engines: {node: '>= 0.12'} + form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} @@ -3788,6 +3911,9 @@ packages: resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} engines: {node: '>= 0.4'} + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} @@ -3796,6 +3922,14 @@ packages: engines: {node: '>=10'} deprecated: This package is no longer supported. + gaxios@6.7.1: + resolution: {integrity: sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==} + engines: {node: '>=14'} + + gcp-metadata@6.1.1: + resolution: {integrity: sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==} + engines: {node: '>=14'} + generate-password@1.7.1: resolution: {integrity: sha512-9bVYY+16m7W7GczRBDqXE+VVuCX+bWNrfYKC/2p2JkZukFb2sKxT6E3zZ3mJGz7GMe5iRK0A/WawSL3jQfJuNQ==} @@ -3882,6 +4016,18 @@ packages: resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} engines: {node: '>=10'} + google-auth-library@9.15.1: + resolution: {integrity: sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==} + engines: {node: '>=14'} + + google-gax@4.6.1: + resolution: {integrity: sha512-V6eky/xz2mcKfAd1Ioxyd6nmA61gao3n01C+YeuIwu3vzM9EDR6wcVzMSIbLMDXWeoi9SHYctXuKYC5uJUT3eQ==} + engines: {node: '>=14'} + + google-logging-utils@0.0.2: + resolution: {integrity: sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==} + engines: {node: '>=14'} + gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -3892,6 +4038,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + gtoken@7.1.0: + resolution: {integrity: sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==} + engines: {node: '>=14.0.0'} + handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -3951,6 +4101,9 @@ packages: hpagent@0.1.2: resolution: {integrity: sha512-ePqFXHtSQWAFXYmj+JtOTHr84iNrII4/QRlAAPPE+zqnKy4xJo7Ie1Y4kC7AdB+LxLxSTTzBMASsEcy0q8YyvQ==} + html-entities@2.6.0: + resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} + html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3970,6 +4123,13 @@ packages: http-headers@3.0.2: resolution: {integrity: sha512-87E1I+2Wg4dxxz4rcxElo3dxO/w1ZtgL1yA0Sb6vH3qU16vRKq1NjWQv9SCY3ly2OQROcoxHZOUpmelS+k6wOw==} + http-parser-js@0.5.10: + resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -4453,6 +4613,9 @@ packages: engines: {node: '>=6'} hasBin: true + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -4800,6 +4963,11 @@ packages: engines: {node: '>=4.0.0'} hasBin: true + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -4983,6 +5151,10 @@ packages: encoding: optional: true + node-forge@1.3.3: + resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} + engines: {node: '>= 6.13.0'} + node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true @@ -5395,6 +5567,10 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + proto3-json-serializer@2.0.2: + resolution: {integrity: sha512-SAzp/O4Yh02jGdRc+uIrGoe87dkN/XtwxfZ4ZyafJHymd79ozp5VG5nyZ7ygqPM5+cpLDjjGnYFUkngonyDPOQ==} + engines: {node: '>=14.0.0'} + protobufjs@7.5.4: resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==} engines: {node: '>=12.0.0'} @@ -5446,12 +5622,12 @@ packages: puppeteer@21.0.1: resolution: {integrity: sha512-KTjmSdPZ6bMkq3EbAzAUhcB3gMDXvdwd6912rxG9hNtjwRJzHSA568vh6vIbO2WQeNmozRdt1LtiUMLSWfeMrg==} engines: {node: '>=16.3.0'} - deprecated: < 24.10.2 is no longer supported + deprecated: < 24.15.0 is no longer supported puppeteer@21.11.0: resolution: {integrity: sha512-9jTHuYe22TD3sNxy0nEIzC7ZrlRnDgeX3xPkbS7PnbdwYjl2o/z/YuCrRBwezdKpbTDTJ4VqIggzNyeRcKq3cg==} engines: {node: '>=16.13.2'} - deprecated: < 24.10.2 is no longer supported + deprecated: < 24.15.0 is no longer supported hasBin: true pure-rand@6.1.0: @@ -5624,6 +5800,10 @@ packages: restructure@2.0.1: resolution: {integrity: sha512-e0dOpjm5DseomnXx2M5lpdZ5zoHqF1+bqdMJUohoYVVQa7cBdnk7fdmeI6byNWP/kiME72EeTiSypTCVnpLiDg==} + retry-request@7.0.2: + resolution: {integrity: sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==} + engines: {node: '>=14'} + retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -5910,6 +6090,12 @@ packages: stream-chopper@3.0.1: resolution: {integrity: sha512-f7h+ly8baAE26iIjcp3VbnBkbIRGtrvV0X0xxFM/d7fwLTYnLzDPTXRKNxa2HZzohOrc96NTrR+FaV3mzOelNA==} + stream-events@1.0.5: + resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + + stream-shift@1.0.3: + resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -5979,6 +6165,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@1.1.2: + resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + strnum@2.1.1: resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} @@ -5986,6 +6175,9 @@ packages: resolution: {integrity: sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==} engines: {node: '>=18'} + stubs@3.0.0: + resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} + superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} @@ -6045,6 +6237,11 @@ packages: tar@6.2.1: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me + + teeny-request@9.0.0: + resolution: {integrity: sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==} + engines: {node: '>=14'} telejson@7.2.0: resolution: {integrity: sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==} @@ -6508,6 +6705,14 @@ packages: webpack-cli: optional: true + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -7355,11 +7560,110 @@ snapshots: '@eslint/js@8.57.1': {} + '@fastify/busboy@3.2.0': {} + + '@firebase/app-check-interop-types@0.3.3': {} + + '@firebase/app-types@0.9.3': {} + + '@firebase/auth-interop-types@0.2.4': {} + + '@firebase/component@0.7.0': + dependencies: + '@firebase/util': 1.13.0 + tslib: 2.8.1 + + '@firebase/database-compat@2.1.0': + dependencies: + '@firebase/component': 0.7.0 + '@firebase/database': 1.1.0 + '@firebase/database-types': 1.0.16 + '@firebase/logger': 0.5.0 + '@firebase/util': 1.13.0 + tslib: 2.8.1 + + '@firebase/database-types@1.0.16': + dependencies: + '@firebase/app-types': 0.9.3 + '@firebase/util': 1.13.0 + + '@firebase/database@1.1.0': + dependencies: + '@firebase/app-check-interop-types': 0.3.3 + '@firebase/auth-interop-types': 0.2.4 + '@firebase/component': 0.7.0 + '@firebase/logger': 0.5.0 + '@firebase/util': 1.13.0 + faye-websocket: 0.11.4 + tslib: 2.8.1 + + '@firebase/logger@0.5.0': + dependencies: + tslib: 2.8.1 + + '@firebase/util@1.13.0': + dependencies: + tslib: 2.8.1 + + '@google-cloud/firestore@7.11.6': + dependencies: + '@opentelemetry/api': 1.9.0 + fast-deep-equal: 3.1.3 + functional-red-black-tree: 1.0.1 + google-gax: 4.6.1 + protobufjs: 7.5.4 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@google-cloud/paginator@5.0.2': + dependencies: + arrify: 2.0.1 + extend: 3.0.2 + optional: true + + '@google-cloud/projectify@4.0.0': + optional: true + + '@google-cloud/promisify@4.0.0': + optional: true + + '@google-cloud/storage@7.18.0': + dependencies: + '@google-cloud/paginator': 5.0.2 + '@google-cloud/projectify': 4.0.0 + '@google-cloud/promisify': 4.0.0 + abort-controller: 3.0.0 + async-retry: 1.3.3 + duplexify: 4.1.3 + fast-xml-parser: 4.5.3 + gaxios: 6.7.1 + google-auth-library: 9.15.1 + html-entities: 2.6.0 + mime: 3.0.0 + p-limit: 3.1.0 + retry-request: 7.0.2 + teeny-request: 9.0.0 + uuid: 8.3.2 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + '@grpc/grpc-js@1.14.0': dependencies: '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 + '@grpc/proto-loader@0.7.15': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.3.2 + protobufjs: 7.5.4 + yargs: 17.7.2 + optional: true + '@grpc/proto-loader@0.8.0': dependencies: lodash.camelcase: 4.3.0 @@ -8761,6 +9065,9 @@ snapshots: '@tokenizer/token@0.3.0': {} + '@tootallnate/once@2.0.0': + optional: true + '@tootallnate/quickjs-emscripten@0.23.0': {} '@tsconfig/node10@1.0.11': {} @@ -8801,6 +9108,9 @@ snapshots: '@types/connect': 3.4.38 '@types/node': 20.19.17 + '@types/caseless@0.12.5': + optional: true + '@types/connect@3.4.38': dependencies: '@types/node': 20.19.17 @@ -8877,6 +9187,9 @@ snapshots: dependencies: '@types/node': 20.19.17 + '@types/long@4.0.2': + optional: true + '@types/luxon@3.7.1': {} '@types/methods@1.1.4': {} @@ -8928,6 +9241,14 @@ snapshots: '@types/range-parser@1.2.7': {} + '@types/request@2.48.13': + dependencies: + '@types/caseless': 0.12.5 + '@types/node': 20.19.17 + '@types/tough-cookie': 4.0.5 + form-data: 2.5.5 + optional: true + '@types/retry@0.12.5': {} '@types/semver@7.7.1': {} @@ -8956,6 +9277,9 @@ snapshots: dependencies: '@types/superagent': 8.1.9 + '@types/tough-cookie@4.0.5': + optional: true + '@types/triple-beam@1.3.5': {} '@types/validator@13.15.3': {} @@ -9183,6 +9507,11 @@ snapshots: abbrev@1.1.1: {} + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + optional: true + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -9369,6 +9698,9 @@ snapshots: get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 + arrify@2.0.1: + optional: true + asap@2.0.6: {} asn1.js@5.4.1: @@ -9548,6 +9880,8 @@ snapshots: big-integer@1.6.52: {} + bignumber.js@9.3.1: {} + binary-search@1.3.6: optional: true @@ -10172,6 +10506,14 @@ snapshots: dependencies: readable-stream: 2.3.8 + duplexify@4.1.3: + dependencies: + end-of-stream: 1.4.5 + inherits: 2.0.4 + readable-stream: 3.6.2 + stream-shift: 1.0.3 + optional: true + eastasianwidth@0.2.0: {} ecc-jsbn@0.1.2: @@ -10604,6 +10946,9 @@ snapshots: etag@1.8.1: {} + event-target-shim@5.0.1: + optional: true + eventemitter3@5.0.1: {} events-universal@1.0.1: @@ -10718,8 +11063,7 @@ snapshots: transitivePeerDependencies: - supports-color - extend@3.0.2: - optional: true + extend@3.0.2: {} extract-zip@1.7.0: dependencies: @@ -10744,6 +11088,8 @@ snapshots: extsprintf@1.3.0: optional: true + farmhash-modern@1.1.0: {} + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -10776,6 +11122,11 @@ snapshots: fast-uri@3.1.0: {} + fast-xml-parser@4.5.3: + dependencies: + strnum: 1.1.2 + optional: true + fast-xml-parser@5.2.5: dependencies: strnum: 2.1.1 @@ -10784,6 +11135,10 @@ snapshots: dependencies: reusify: 1.1.0 + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -10850,6 +11205,26 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 + firebase-admin@13.6.0: + dependencies: + '@fastify/busboy': 3.2.0 + '@firebase/database-compat': 2.1.0 + '@firebase/database-types': 1.0.16 + '@types/node': 22.19.0 + farmhash-modern: 1.1.0 + fast-deep-equal: 3.1.3 + google-auth-library: 9.15.1 + jsonwebtoken: 9.0.2 + jwks-rsa: 3.2.0 + node-forge: 1.3.3 + uuid: 11.1.0 + optionalDependencies: + '@google-cloud/firestore': 7.11.6 + '@google-cloud/storage': 7.18.0 + transitivePeerDependencies: + - encoding + - supports-color + flat-cache@3.2.0: dependencies: flatted: 3.3.3 @@ -10913,6 +11288,16 @@ snapshots: mime-types: 2.1.35 optional: true + form-data@2.5.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + safe-buffer: 5.2.1 + optional: true + form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -10980,6 +11365,9 @@ snapshots: hasown: 2.0.2 is-callable: 1.2.7 + functional-red-black-tree@1.0.1: + optional: true + functions-have-names@1.2.3: {} gauge@3.0.2: @@ -10994,6 +11382,26 @@ snapshots: strip-ansi: 6.0.1 wide-align: 1.1.5 + gaxios@6.7.1: + dependencies: + extend: 3.0.2 + https-proxy-agent: 7.0.6 + is-stream: 2.0.1 + node-fetch: 2.7.0 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + + gcp-metadata@6.1.1: + dependencies: + gaxios: 6.7.1 + google-logging-utils: 0.0.2 + json-bigint: 1.0.0 + transitivePeerDependencies: + - encoding + - supports-color + generate-password@1.7.1: {} gensync@1.0.0-beta.2: {} @@ -11109,12 +11517,53 @@ snapshots: merge2: 1.4.1 slash: 3.0.0 + google-auth-library@9.15.1: + dependencies: + base64-js: 1.5.1 + ecdsa-sig-formatter: 1.0.11 + gaxios: 6.7.1 + gcp-metadata: 6.1.1 + gtoken: 7.1.0 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + + google-gax@4.6.1: + dependencies: + '@grpc/grpc-js': 1.14.0 + '@grpc/proto-loader': 0.7.15 + '@types/long': 4.0.2 + abort-controller: 3.0.0 + duplexify: 4.1.3 + google-auth-library: 9.15.1 + node-fetch: 2.7.0 + object-hash: 3.0.0 + proto3-json-serializer: 2.0.2 + protobufjs: 7.5.4 + retry-request: 7.0.2 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + google-logging-utils@0.0.2: {} + gopd@1.2.0: {} graceful-fs@4.2.11: {} graphemer@1.4.0: {} + gtoken@7.1.0: + dependencies: + gaxios: 6.7.1 + jws: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -11169,6 +11618,9 @@ snapshots: hpagent@0.1.2: {} + html-entities@2.6.0: + optional: true + html-escaper@2.0.2: {} html-pdf@3.0.1: @@ -11192,6 +11644,17 @@ snapshots: next-line: 1.1.0 optional: true + http-parser-js@0.5.10: {} + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.0 + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + optional: true + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -11878,6 +12341,10 @@ snapshots: jsesc@3.1.0: {} + json-bigint@1.0.0: + dependencies: + bignumber.js: 9.3.1 + json-buffer@3.0.1: {} json-parse-even-better-errors@2.3.1: {} @@ -12225,6 +12692,9 @@ snapshots: mime@2.6.0: {} + mime@3.0.0: + optional: true + mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} @@ -12392,6 +12862,8 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-forge@1.3.3: {} + node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.0 @@ -12824,6 +13296,11 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + proto3-json-serializer@2.0.2: + dependencies: + protobufjs: 7.5.4 + optional: true + protobufjs@7.5.4: dependencies: '@protobufjs/aspromise': 1.1.2 @@ -13144,6 +13621,16 @@ snapshots: restructure@2.0.1: {} + retry-request@7.0.2: + dependencies: + '@types/request': 2.48.13 + extend: 3.0.2 + teeny-request: 9.0.0 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + retry@0.13.1: {} reusify@1.1.0: {} @@ -13510,6 +13997,14 @@ snapshots: readable-stream: 3.6.2 optional: true + stream-events@1.0.5: + dependencies: + stubs: 3.0.0 + optional: true + + stream-shift@1.0.3: + optional: true + streamsearch@1.1.0: {} streamx@2.23.0: @@ -13588,12 +14083,18 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@1.1.2: + optional: true + strnum@2.1.1: {} strtok3@10.3.4: dependencies: '@tokenizer/token': 0.3.0 + stubs@3.0.0: + optional: true + superagent@7.1.6: dependencies: component-emitter: 1.3.1 @@ -13693,6 +14194,18 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 + teeny-request@9.0.0: + dependencies: + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + stream-events: 1.0.5 + uuid: 9.0.1 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + telejson@7.2.0: dependencies: memoizerific: 1.11.3 @@ -14160,6 +14673,14 @@ snapshots: - esbuild - uglify-js + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.10 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3