feat(service): P2.3 Socket.io /message gateway + real SessionVerifier
open_thread (create-or-join) / send_message / read / delivered / typing; HS256 token verified on connect (SessionVerifier reuses AppTokenVerifier pattern); OutboxBus bridge propagates REST/ingest messages to rooms (dedup vs inline emit). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,10 @@
|
||||
"@nestjs/common": "^11.1.27",
|
||||
"@nestjs/core": "^11.1.27",
|
||||
"@nestjs/platform-express": "^11.1.27",
|
||||
"@nestjs/platform-socket.io": "^11.1.27",
|
||||
"@nestjs/websockets": "^11.1.27",
|
||||
"@prisma/client": "^6.2.1",
|
||||
"socket.io": "^4.8.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.15.1",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import {
|
||||
ConnectedSocket,
|
||||
MessageBody,
|
||||
OnGatewayConnection,
|
||||
OnGatewayInit,
|
||||
SubscribeMessage,
|
||||
WebSocketGateway,
|
||||
WebSocketServer,
|
||||
} from '@nestjs/websockets';
|
||||
import { Server, Socket } from 'socket.io';
|
||||
import { IIOS_EVENTS } from '@insignia/iios-contracts';
|
||||
import { MessageService, type MessagePrincipal } from './message.service';
|
||||
import { SessionVerifier } from '../platform/session.verifier';
|
||||
import { OutboxBus } from '../outbox/outbox.bus';
|
||||
|
||||
interface SocketState {
|
||||
principal: MessagePrincipal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Realtime messaging over Socket.io `/message` (reuses the support-service
|
||||
* gateway pattern). Token verified synchronously on connect. Send writes via
|
||||
* MessageService and emits inline to the thread room; the OutboxBus bridge
|
||||
* propagates messages persisted elsewhere (REST/ingest) to connected clients.
|
||||
*/
|
||||
@WebSocketGateway({ namespace: '/message', cors: { origin: true } })
|
||||
export class MessageGateway implements OnGatewayInit, OnGatewayConnection {
|
||||
private readonly logger = new Logger(MessageGateway.name);
|
||||
private readonly emittedLocally = new Set<string>();
|
||||
|
||||
@WebSocketServer() server!: Server;
|
||||
|
||||
constructor(
|
||||
private readonly messages: MessageService,
|
||||
private readonly session: SessionVerifier,
|
||||
private readonly bus: OutboxBus,
|
||||
) {}
|
||||
|
||||
afterInit(): void {
|
||||
this.bus.on(IIOS_EVENTS.messageSent, (payload) => {
|
||||
const ce = payload as { data?: { interactionId?: string; threadId?: string } };
|
||||
const interactionId = ce?.data?.interactionId;
|
||||
const threadId = ce?.data?.threadId;
|
||||
if (!interactionId || !threadId) return;
|
||||
if (this.emittedLocally.delete(interactionId)) return; // already emitted inline
|
||||
void this.messages.getMessageById(interactionId).then((m) => {
|
||||
if (m) this.server.to(threadId).emit('message', m);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleConnection(client: Socket): void {
|
||||
try {
|
||||
const token = String(client.handshake.auth?.token ?? '');
|
||||
const principal = this.session.verify(token);
|
||||
client.data = { principal } satisfies SocketState;
|
||||
} catch (err) {
|
||||
this.logger.warn(`rejecting socket: ${(err as Error).message}`);
|
||||
client.disconnect(true);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeMessage('open_thread')
|
||||
async openThread(@ConnectedSocket() client: Socket, @MessageBody() body: { threadId?: string }) {
|
||||
const { principal } = client.data as SocketState;
|
||||
const result = await this.messages.openThread(body?.threadId ?? null, principal);
|
||||
await client.join(result.threadId);
|
||||
return result;
|
||||
}
|
||||
|
||||
@SubscribeMessage('send_message')
|
||||
async sendMessage(
|
||||
@ConnectedSocket() client: Socket,
|
||||
@MessageBody() body: { threadId: string; content: string; contentRef?: string },
|
||||
) {
|
||||
const { principal } = client.data as SocketState;
|
||||
const msg = await this.messages.send(
|
||||
body.threadId,
|
||||
principal,
|
||||
{ content: body.content, contentRef: body.contentRef },
|
||||
randomUUID(),
|
||||
);
|
||||
this.emittedLocally.add(msg.id);
|
||||
this.server.to(body.threadId).emit('message', msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
@SubscribeMessage('read')
|
||||
async read(@ConnectedSocket() client: Socket, @MessageBody() body: { threadId: string; interactionId: string }) {
|
||||
const { principal } = client.data as SocketState;
|
||||
const r = await this.messages.markRead(body.threadId, principal, body.interactionId);
|
||||
this.server.to(body.threadId).emit('receipt', { interactionId: r.interactionId, actorId: r.actorId, kind: 'READ' });
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
@SubscribeMessage('delivered')
|
||||
async delivered(@ConnectedSocket() client: Socket, @MessageBody() body: { threadId: string; interactionId: string }) {
|
||||
const { principal } = client.data as SocketState;
|
||||
const r = await this.messages.markDelivered(body.threadId, principal, body.interactionId);
|
||||
this.server.to(body.threadId).emit('receipt', { interactionId: r.interactionId, actorId: r.actorId, kind: 'DELIVERED' });
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
@SubscribeMessage('typing')
|
||||
typing(@ConnectedSocket() client: Socket, @MessageBody() body: { threadId: string }): void {
|
||||
const { principal } = client.data as SocketState;
|
||||
client.to(body.threadId).emit('typing', { threadId: body.threadId, userId: principal.userId });
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { MessageService } from './message.service';
|
||||
import { MessageGateway } from './message.gateway';
|
||||
import { SessionVerifier } from '../platform/session.verifier';
|
||||
import { OutboxModule } from '../outbox/outbox.module';
|
||||
|
||||
@Module({
|
||||
providers: [MessageService],
|
||||
exports: [MessageService],
|
||||
imports: [OutboxModule],
|
||||
providers: [MessageService, MessageGateway, SessionVerifier],
|
||||
exports: [MessageService, SessionVerifier],
|
||||
})
|
||||
export class MessageModule {}
|
||||
|
||||
@@ -183,6 +183,31 @@ export class MessageService {
|
||||
return { interactionId, actorId: actor.id };
|
||||
}
|
||||
|
||||
async markDelivered(
|
||||
threadId: string,
|
||||
principal: MessagePrincipal,
|
||||
interactionId: string,
|
||||
): Promise<{ interactionId: string; actorId: string }> {
|
||||
const thread = await this.prisma.iiosThread.findUnique({ where: { id: threadId } });
|
||||
if (!thread) throw new NotFoundException('thread not found');
|
||||
const actor = await this.resolveActor(thread.scopeId, principal);
|
||||
await this.prisma.iiosMessageReceipt.upsert({
|
||||
where: { interactionId_actorId_receiptKind: { interactionId, actorId: actor.id, receiptKind: 'DELIVERED' } },
|
||||
create: { interactionId, actorId: actor.id, receiptKind: 'DELIVERED' },
|
||||
update: { occurredAt: new Date() },
|
||||
});
|
||||
return { interactionId, actorId: actor.id };
|
||||
}
|
||||
|
||||
async getMessageById(id: string): Promise<MessageDto | null> {
|
||||
const i = await this.prisma.iiosInteraction.findUnique({
|
||||
where: { id },
|
||||
include: { parts: { orderBy: { partIndex: 'asc' } } },
|
||||
});
|
||||
if (!i || !i.threadId) return null;
|
||||
return this.toDto(i, i.threadId);
|
||||
}
|
||||
|
||||
async history(threadId: string): Promise<MessageDto[]> {
|
||||
const interactions = await this.prisma.iiosInteraction.findMany({
|
||||
where: { threadId },
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import type { MessagePrincipal } from '../messaging/message.service';
|
||||
|
||||
/**
|
||||
* The real `session` port for P2: verifies a host app's HS256 token (reuses the
|
||||
* support-service AppTokenVerifier pattern). Per-app secrets come from the
|
||||
* APP_SECRETS env (JSON map keyed by appId). Expected claims: { sub, name?,
|
||||
* appId, orgId?, tenantId? }.
|
||||
*/
|
||||
@Injectable()
|
||||
export class SessionVerifier {
|
||||
private readonly secrets: Record<string, string>;
|
||||
|
||||
constructor() {
|
||||
try {
|
||||
this.secrets = JSON.parse(process.env.APP_SECRETS ?? '{}');
|
||||
} catch {
|
||||
this.secrets = {};
|
||||
}
|
||||
}
|
||||
|
||||
verify(token: string): MessagePrincipal {
|
||||
const decoded = jwt.decode(token) as jwt.JwtPayload | null;
|
||||
const appId = decoded?.appId ? String(decoded.appId) : undefined;
|
||||
if (!appId) throw new UnauthorizedException('missing appId claim');
|
||||
const secret = this.secrets[appId];
|
||||
if (!secret) throw new UnauthorizedException(`unknown app: ${appId}`);
|
||||
|
||||
let payload: jwt.JwtPayload;
|
||||
try {
|
||||
payload = jwt.verify(token, secret, { algorithms: ['HS256'] }) as jwt.JwtPayload;
|
||||
} catch {
|
||||
throw new UnauthorizedException('invalid app token');
|
||||
}
|
||||
if (!payload.sub) throw new UnauthorizedException('missing sub claim');
|
||||
|
||||
return {
|
||||
userId: String(payload.sub),
|
||||
appId,
|
||||
orgId: payload.orgId ? String(payload.orgId) : `org_${appId}`,
|
||||
tenantId: payload.tenantId ? String(payload.tenantId) : undefined,
|
||||
displayName: payload.name ? String(payload.name) : undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
Generated
+184
-3
@@ -30,10 +30,16 @@ importers:
|
||||
version: 11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core':
|
||||
specifier: ^11.1.27
|
||||
version: 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
version: 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(@nestjs/websockets@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/platform-express':
|
||||
specifier: ^11.1.27
|
||||
version: 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)
|
||||
'@nestjs/platform-socket.io':
|
||||
specifier: ^11.1.27
|
||||
version: 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.27)(rxjs@7.8.2)
|
||||
'@nestjs/websockets':
|
||||
specifier: ^11.1.27
|
||||
version: 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@prisma/client':
|
||||
specifier: ^6.2.1
|
||||
version: 6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)
|
||||
@@ -52,6 +58,9 @@ importers:
|
||||
rxjs:
|
||||
specifier: ^7.8.2
|
||||
version: 7.8.2
|
||||
socket.io:
|
||||
specifier: ^4.8.3
|
||||
version: 4.8.3
|
||||
devDependencies:
|
||||
'@insignia/iios-testkit':
|
||||
specifier: workspace:*
|
||||
@@ -501,6 +510,13 @@ packages:
|
||||
'@nestjs/common': ^11.0.0
|
||||
'@nestjs/core': ^11.0.0
|
||||
|
||||
'@nestjs/platform-socket.io@11.1.27':
|
||||
resolution: {integrity: sha512-xgpLzaIDGOCC6xOAtHnRAz8sqieFgGxxu3MN5ID026Jt6oeL3efp29N5QHhPr7UlqBfy/Jd02uj0POkZq6Au3Q==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^11.0.0
|
||||
'@nestjs/websockets': ^11.0.0
|
||||
rxjs: ^7.1.0
|
||||
|
||||
'@nestjs/schematics@11.1.0':
|
||||
resolution: {integrity: sha512-lVxGZ46tcdItFMoXr6vyKWlnOsm1SZm/GUqAEDvy2RL4Q4O+3bkziAhrO7Y8JLssFUUvNFEGqAizI52WAxhjDw==}
|
||||
peerDependencies:
|
||||
@@ -510,6 +526,18 @@ packages:
|
||||
prettier:
|
||||
optional: true
|
||||
|
||||
'@nestjs/websockets@11.1.27':
|
||||
resolution: {integrity: sha512-X3OgJt9KgYTvt9D7sNz9SOj3A1daAHy7DZrYhM1pky8Fh+erlKQH5IQ/tKm+GaJKA5M0srBUr1CMqjak/qNxOw==}
|
||||
peerDependencies:
|
||||
'@nestjs/common': ^11.0.0
|
||||
'@nestjs/core': ^11.0.0
|
||||
'@nestjs/platform-socket.io': ^11.0.0
|
||||
reflect-metadata: ^0.1.12 || ^0.2.0
|
||||
rxjs: ^7.1.0
|
||||
peerDependenciesMeta:
|
||||
'@nestjs/platform-socket.io':
|
||||
optional: true
|
||||
|
||||
'@prisma/client@6.19.3':
|
||||
resolution: {integrity: sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==}
|
||||
engines: {node: '>=18.18'}
|
||||
@@ -665,6 +693,9 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@socket.io/component-emitter@3.1.2':
|
||||
resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==}
|
||||
|
||||
'@standard-schema/spec@1.1.0':
|
||||
resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==}
|
||||
|
||||
@@ -684,6 +715,9 @@ packages:
|
||||
'@types/connect@3.4.38':
|
||||
resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==}
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
@@ -732,6 +766,9 @@ packages:
|
||||
'@types/validator@13.15.10':
|
||||
resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
|
||||
|
||||
'@vitest/expect@3.2.6':
|
||||
resolution: {integrity: sha512-1+7q9BtaKzEmO+fmNT3kYvoNn5Y71XWAx2Q5HRim4tTVRQVRv4uJFAQ5FbK0OPUeNP/WmVCpxYxoJdvuHVjzBQ==}
|
||||
|
||||
@@ -812,6 +849,10 @@ packages:
|
||||
'@xtuc/long@4.2.2':
|
||||
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
|
||||
|
||||
accepts@1.3.8:
|
||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
accepts@2.0.0:
|
||||
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -901,6 +942,10 @@ packages:
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
base64id@2.0.0:
|
||||
resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
|
||||
engines: {node: ^4.5.0 || >= 5.9}
|
||||
|
||||
baseline-browser-mapping@2.10.40:
|
||||
resolution: {integrity: sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@@ -1154,6 +1199,14 @@ packages:
|
||||
resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
engine.io-parser@5.2.3:
|
||||
resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
engine.io@6.6.9:
|
||||
resolution: {integrity: sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==}
|
||||
engines: {node: '>=10.2.0'}
|
||||
|
||||
enhanced-resolve@5.24.1:
|
||||
resolution: {integrity: sha512-7DdUaTjmNwMcH2gLr1qycesKII3BK4RLy/mdAb7x10Lq7bR4aNKHt1BR1ZALSv0rPM/hF5wYF0PhGop/rJm8vw==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@@ -1555,6 +1608,10 @@ packages:
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
hasBin: true
|
||||
|
||||
negotiator@0.6.3:
|
||||
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
negotiator@1.0.0:
|
||||
resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -1584,6 +1641,10 @@ packages:
|
||||
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
object-hash@3.0.0:
|
||||
resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
object-inspect@1.13.4:
|
||||
resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -1787,6 +1848,17 @@ packages:
|
||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
socket.io-adapter@2.5.8:
|
||||
resolution: {integrity: sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==}
|
||||
|
||||
socket.io-parser@4.2.6:
|
||||
resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
|
||||
socket.io@4.8.3:
|
||||
resolution: {integrity: sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==}
|
||||
engines: {node: '>=10.2.0'}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -2112,6 +2184,18 @@ packages:
|
||||
wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
|
||||
ws@8.21.0:
|
||||
resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==}
|
||||
engines: {node: '>=10.0.0'}
|
||||
peerDependencies:
|
||||
bufferutil: ^4.0.1
|
||||
utf-8-validate: '>=5.0.2'
|
||||
peerDependenciesMeta:
|
||||
bufferutil:
|
||||
optional: true
|
||||
utf-8-validate:
|
||||
optional: true
|
||||
|
||||
yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2479,7 +2563,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@nestjs/core@11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
|
||||
'@nestjs/core@11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(@nestjs/websockets@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
fast-safe-stringify: 2.1.1
|
||||
@@ -2491,11 +2575,12 @@ snapshots:
|
||||
uid: 2.0.2
|
||||
optionalDependencies:
|
||||
'@nestjs/platform-express': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)
|
||||
'@nestjs/websockets': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
|
||||
'@nestjs/platform-express@11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(@nestjs/websockets@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
cors: 2.8.6
|
||||
express: 5.2.1
|
||||
multer: 2.1.1
|
||||
@@ -2504,6 +2589,18 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@nestjs/platform-socket.io@11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.27)(rxjs@7.8.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/websockets': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
rxjs: 7.8.2
|
||||
socket.io: 4.8.3
|
||||
tslib: 2.8.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
'@nestjs/schematics@11.1.0(chokidar@4.0.3)(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@angular-devkit/core': 19.2.24(chokidar@4.0.3)
|
||||
@@ -2515,6 +2612,18 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- chokidar
|
||||
|
||||
'@nestjs/websockets@11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.27)(@nestjs/platform-socket.io@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)':
|
||||
dependencies:
|
||||
'@nestjs/common': 11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
'@nestjs/core': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.27)(@nestjs/websockets@11.1.27)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
iterare: 1.2.1
|
||||
object-hash: 3.0.0
|
||||
reflect-metadata: 0.2.2
|
||||
rxjs: 7.8.2
|
||||
tslib: 2.8.1
|
||||
optionalDependencies:
|
||||
'@nestjs/platform-socket.io': 11.1.27(@nestjs/common@11.1.27(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/websockets@11.1.27)(rxjs@7.8.2)
|
||||
|
||||
'@prisma/client@6.19.3(prisma@6.19.3(typescript@5.9.3))(typescript@5.9.3)':
|
||||
optionalDependencies:
|
||||
prisma: 6.19.3(typescript@5.9.3)
|
||||
@@ -2625,6 +2734,8 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc@4.62.2':
|
||||
optional: true
|
||||
|
||||
'@socket.io/component-emitter@3.1.2': {}
|
||||
|
||||
'@standard-schema/spec@1.1.0': {}
|
||||
|
||||
'@tokenizer/inflate@0.4.1':
|
||||
@@ -2650,6 +2761,10 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
'@types/cors@2.8.19':
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/eslint-scope@3.7.7':
|
||||
@@ -2707,6 +2822,10 @@ snapshots:
|
||||
|
||||
'@types/validator@13.15.10': {}
|
||||
|
||||
'@types/ws@8.18.1':
|
||||
dependencies:
|
||||
'@types/node': 26.0.1
|
||||
|
||||
'@vitest/expect@3.2.6':
|
||||
dependencies:
|
||||
'@types/chai': 5.2.3
|
||||
@@ -2829,6 +2948,11 @@ snapshots:
|
||||
|
||||
'@xtuc/long@4.2.2': {}
|
||||
|
||||
accepts@1.3.8:
|
||||
dependencies:
|
||||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
|
||||
accepts@2.0.0:
|
||||
dependencies:
|
||||
mime-types: 3.0.2
|
||||
@@ -2902,6 +3026,8 @@ snapshots:
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
base64id@2.0.0: {}
|
||||
|
||||
baseline-browser-mapping@2.10.40: {}
|
||||
|
||||
bl@4.1.0:
|
||||
@@ -3139,6 +3265,25 @@ snapshots:
|
||||
|
||||
encodeurl@2.0.0: {}
|
||||
|
||||
engine.io-parser@5.2.3: {}
|
||||
|
||||
engine.io@6.6.9:
|
||||
dependencies:
|
||||
'@types/cors': 2.8.19
|
||||
'@types/node': 26.0.1
|
||||
'@types/ws': 8.18.1
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cookie: 0.7.2
|
||||
cors: 2.8.6
|
||||
debug: 4.4.3
|
||||
engine.io-parser: 5.2.3
|
||||
ws: 8.21.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
enhanced-resolve@5.24.1:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@@ -3555,6 +3700,8 @@ snapshots:
|
||||
|
||||
nanoid@3.3.15: {}
|
||||
|
||||
negotiator@0.6.3: {}
|
||||
|
||||
negotiator@1.0.0: {}
|
||||
|
||||
neo-async@2.6.2: {}
|
||||
@@ -3577,6 +3724,8 @@ snapshots:
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-hash@3.0.0: {}
|
||||
|
||||
object-inspect@1.13.4: {}
|
||||
|
||||
ohash@2.0.11: {}
|
||||
@@ -3836,6 +3985,36 @@ snapshots:
|
||||
|
||||
signal-exit@4.1.0: {}
|
||||
|
||||
socket.io-adapter@2.5.8:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
ws: 8.21.0
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
socket.io-parser@4.2.6:
|
||||
dependencies:
|
||||
'@socket.io/component-emitter': 3.1.2
|
||||
debug: 4.4.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
socket.io@4.8.3:
|
||||
dependencies:
|
||||
accepts: 1.3.8
|
||||
base64id: 2.0.0
|
||||
cors: 2.8.6
|
||||
debug: 4.4.3
|
||||
engine.io: 6.6.9
|
||||
socket.io-adapter: 2.5.8
|
||||
socket.io-parser: 4.2.6
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
@@ -4130,6 +4309,8 @@ snapshots:
|
||||
|
||||
wrappy@1.0.2: {}
|
||||
|
||||
ws@8.21.0: {}
|
||||
|
||||
yargs-parser@21.1.1: {}
|
||||
|
||||
yoctocolors-cjs@2.1.3: {}
|
||||
|
||||
Reference in New Issue
Block a user