fix: distinguish between session / user scoped (mobile) connections
This commit is contained in:
parent
1ace32e5c3
commit
0b3017ef1b
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,4 +1,7 @@
|
||||
node_modules
|
||||
.env
|
||||
dist
|
||||
.pgdata
|
||||
.pgdata
|
||||
|
||||
.env.local
|
||||
.env
|
@ -1,22 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
container_name: handy_postgres
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: handy
|
||||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
@ -8,6 +8,7 @@
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit",
|
||||
"start": "tsx ./sources/main.ts",
|
||||
"dev": "lsof -ti tcp:3005 | xargs kill -9 && tsx --env-file=.env.local ./sources/main.ts",
|
||||
"test": "vitest",
|
||||
"migrate": "prisma migrate dev",
|
||||
"generate": "prisma generate",
|
||||
@ -52,4 +53,4 @@
|
||||
"zod": "^3.24.2",
|
||||
"zod-to-json-schema": "^3.24.3"
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,22 @@ import * as privacyKit from "privacy-kit";
|
||||
import * as tweetnacl from "tweetnacl";
|
||||
import { db } from "@/storage/db";
|
||||
import { Account, Update } from "@prisma/client";
|
||||
import { pubsub } from "@/services/pubsub";
|
||||
|
||||
// Connection metadata types
|
||||
interface SessionScopedConnection {
|
||||
connectionType: 'session-scoped';
|
||||
socket: Socket;
|
||||
userId: string;
|
||||
sessionId: string;
|
||||
}
|
||||
|
||||
interface UserScopedConnection {
|
||||
connectionType: 'user-scoped';
|
||||
socket: Socket;
|
||||
userId: string;
|
||||
}
|
||||
|
||||
type ClientConnection = SessionScopedConnection | UserScopedConnection;
|
||||
|
||||
declare module 'fastify' {
|
||||
interface FastifyRequest {
|
||||
@ -18,6 +33,7 @@ declare module 'fastify' {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export async function startApi() {
|
||||
|
||||
// Configure
|
||||
@ -77,6 +93,42 @@ export async function startApi() {
|
||||
}
|
||||
});
|
||||
|
||||
// Send session update to all relevant connections
|
||||
let emitUpdateToInterestedClients = ({event, userId, sessionId, payload, skipSenderConnection}: {
|
||||
event: string,
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
payload: any,
|
||||
skipSenderConnection?: ClientConnection
|
||||
}) => {
|
||||
const connections = userIdToClientConnections.get(userId);
|
||||
if (!connections) {
|
||||
log({ module: 'websocket', level: 'warn' }, `No connections found for user ${userId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const connection of connections) {
|
||||
// Skip message echo
|
||||
if (skipSenderConnection && connection === skipSenderConnection) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Send to all user-scoped connections - we already matched user
|
||||
if (connection.connectionType === 'user-scoped') {
|
||||
log({ module: 'websocket' }, `Sending ${event} to user-scoped connection ${connection.socket.id}`);
|
||||
connection.socket.emit(event, payload);
|
||||
}
|
||||
|
||||
// Send to all session-scoped connections, only that match sessionId
|
||||
if (connection.connectionType === 'session-scoped'
|
||||
&& connection.sessionId === sessionId
|
||||
) {
|
||||
log({ module: 'websocket' }, `Sending ${event} to session-scoped connection ${connection.socket.id}`);
|
||||
connection.socket.emit(event, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auth schema
|
||||
const authSchema = z.object({
|
||||
publicKey: z.string(),
|
||||
@ -259,7 +311,17 @@ export async function startApi() {
|
||||
});
|
||||
|
||||
// Emit update to connected sockets
|
||||
pubsub.emit('update', userId, result.update);
|
||||
emitUpdateToInterestedClients({
|
||||
event: 'update',
|
||||
userId,
|
||||
sessionId: result.session.id,
|
||||
payload: {
|
||||
id: result.update.id,
|
||||
seq: result.update.seq,
|
||||
body: result.update.content,
|
||||
createdAt: result.update.createdAt.getTime()
|
||||
}
|
||||
});
|
||||
|
||||
return reply.send({
|
||||
session: {
|
||||
@ -352,15 +414,18 @@ export async function startApi() {
|
||||
serveClient: false // Don't serve the client files
|
||||
});
|
||||
|
||||
// Track connected users
|
||||
const userSockets = new Map<string, Set<Socket>>();
|
||||
// Track connections by scope type
|
||||
const userIdToClientConnections = new Map<string, Set<ClientConnection>>();
|
||||
|
||||
// Track RPC listeners: Map<userId, Map<rpcName, Socket>>
|
||||
// Track RPC listeners: Map<userId, Map<rpcMethodWithSessionPrefix, Socket>>
|
||||
// Only session-scoped clients (CLI) register handlers, only user-scoped clients (mobile) call them
|
||||
const rpcListeners = new Map<string, Map<string, Socket>>();
|
||||
|
||||
io.on("connection", async (socket) => {
|
||||
log({ module: 'websocket' }, `New connection attempt from socket: ${socket.id}`);
|
||||
const token = socket.handshake.auth.token as string;
|
||||
const clientType = socket.handshake.auth.clientType as 'session-scoped' | 'user-scoped' | undefined;
|
||||
const sessionId = socket.handshake.auth.sessionId as string | undefined;
|
||||
|
||||
if (!token) {
|
||||
log({ module: 'websocket' }, `No token provided`);
|
||||
@ -369,6 +434,14 @@ export async function startApi() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate session-scoped clients have sessionId
|
||||
if (clientType === 'session-scoped' && !sessionId) {
|
||||
log({ module: 'websocket' }, `Session-scoped client missing sessionId`);
|
||||
socket.emit('error', { message: 'Session ID required for session-scoped clients' });
|
||||
socket.disconnect();
|
||||
return;
|
||||
}
|
||||
|
||||
const verified = await tokenVerifier.verify(token);
|
||||
if (!verified) {
|
||||
log({ module: 'websocket' }, `Invalid token provided`);
|
||||
@ -377,42 +450,38 @@ export async function startApi() {
|
||||
return;
|
||||
}
|
||||
|
||||
log({ module: 'websocket' }, `Token verified: ${verified.user}`);
|
||||
|
||||
const userId = verified.user as string;
|
||||
log({ module: 'websocket' }, `Token verified: ${userId}, clientType: ${clientType || 'user-scoped'}, sessionId: ${sessionId || 'none'}`);
|
||||
|
||||
// Track socket for user
|
||||
if (!userSockets.has(userId)) {
|
||||
userSockets.set(userId, new Set());
|
||||
// Store connection based on type
|
||||
const metadata = { clientType: clientType || 'user-scoped', sessionId };
|
||||
let connection: ClientConnection;
|
||||
if (metadata.clientType === 'session-scoped' && sessionId) {
|
||||
connection = {
|
||||
connectionType: 'session-scoped',
|
||||
socket,
|
||||
userId,
|
||||
sessionId
|
||||
};
|
||||
} else {
|
||||
connection = {
|
||||
connectionType: 'user-scoped',
|
||||
socket,
|
||||
userId
|
||||
};
|
||||
}
|
||||
userSockets.get(userId)!.add(socket);
|
||||
|
||||
// Subscribe to updates for this user
|
||||
const updateHandler = (accountId: string, update: Update) => {
|
||||
if (accountId === userId) {
|
||||
socket.emit('update', {
|
||||
id: update.id,
|
||||
seq: update.seq,
|
||||
body: update.content,
|
||||
createdAt: update.createdAt.getTime()
|
||||
});
|
||||
}
|
||||
};
|
||||
pubsub.on('update', updateHandler);
|
||||
const updateEphemeralHandler = (accountId: string, update: { type: 'activity', id: string, active: boolean, activeAt: number, thinking: boolean }) => {
|
||||
if (accountId === userId) {
|
||||
socket.emit('ephemeral', update);
|
||||
}
|
||||
};
|
||||
pubsub.on('update-ephemeral', updateEphemeralHandler);
|
||||
if (!userIdToClientConnections.has(userId)) {
|
||||
userIdToClientConnections.set(userId, new Set());
|
||||
}
|
||||
userIdToClientConnections.get(userId)!.add(connection);
|
||||
|
||||
socket.on('disconnect', () => {
|
||||
// Clean up
|
||||
const sockets = userSockets.get(userId);
|
||||
if (sockets) {
|
||||
sockets.delete(socket);
|
||||
if (sockets.size === 0) {
|
||||
userSockets.delete(userId);
|
||||
// Cleanup
|
||||
const connections = userIdToClientConnections.get(userId);
|
||||
if (connections) {
|
||||
connections.delete(connection);
|
||||
if (connections.size === 0) {
|
||||
userIdToClientConnections.delete(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -438,8 +507,6 @@ export async function startApi() {
|
||||
}
|
||||
}
|
||||
|
||||
pubsub.off('update', updateHandler);
|
||||
pubsub.off('update-ephemeral', updateEphemeralHandler);
|
||||
log({ module: 'websocket' }, `User disconnected: ${userId}`);
|
||||
});
|
||||
|
||||
@ -471,12 +538,17 @@ export async function startApi() {
|
||||
});
|
||||
|
||||
// Emit update to connected sockets
|
||||
pubsub.emit('update-ephemeral', userId, {
|
||||
type: 'activity',
|
||||
id: sid,
|
||||
active: true,
|
||||
activeAt: t,
|
||||
thinking
|
||||
emitUpdateToInterestedClients({
|
||||
event: 'ephemeral',
|
||||
userId,
|
||||
sessionId: sid,
|
||||
payload: {
|
||||
type: 'activity',
|
||||
id: sid,
|
||||
active: true,
|
||||
activeAt: t,
|
||||
thinking
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -508,18 +580,25 @@ export async function startApi() {
|
||||
});
|
||||
|
||||
// Emit update to connected sockets
|
||||
pubsub.emit('update-ephemeral', userId, {
|
||||
type: 'activity',
|
||||
id: sid,
|
||||
active: false,
|
||||
activeAt: t,
|
||||
thinking: false
|
||||
emitUpdateToInterestedClients({
|
||||
event: 'ephemeral',
|
||||
userId,
|
||||
sessionId: sid,
|
||||
payload: {
|
||||
type: 'activity',
|
||||
id: sid,
|
||||
active: false,
|
||||
activeAt: t,
|
||||
thinking: false
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('message', async (data: any) => {
|
||||
const { sid, message } = data;
|
||||
|
||||
log({ module: 'websocket' }, `Received message from socket ${socket.id}: ${sid} ${message.length} bytes`);
|
||||
|
||||
// Resolve session
|
||||
const session = await db.session.findUnique({
|
||||
where: { id: sid, accountId: userId }
|
||||
@ -613,8 +692,19 @@ export async function startApi() {
|
||||
|
||||
if (!result) return;
|
||||
|
||||
// Emit update to connected sockets
|
||||
pubsub.emit('update', userId, result.update);
|
||||
// Emit update to relevant clients
|
||||
emitUpdateToInterestedClients({
|
||||
event: 'update',
|
||||
userId,
|
||||
sessionId: sid,
|
||||
payload: {
|
||||
id: result.update.id,
|
||||
seq: result.update.seq,
|
||||
body: result.update.content,
|
||||
createdAt: result.update.createdAt.getTime()
|
||||
},
|
||||
skipSenderConnection: connection
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('update-metadata', async (data: any, callback: (response: any) => void) => {
|
||||
@ -695,7 +785,12 @@ export async function startApi() {
|
||||
}
|
||||
|
||||
// Emit update to connected sockets
|
||||
pubsub.emit('update', userId, result.update);
|
||||
emitUpdateToInterestedClients({
|
||||
event: 'update',
|
||||
userId,
|
||||
sessionId: sid,
|
||||
payload: result.update
|
||||
});
|
||||
|
||||
// Send success response with new version via callback
|
||||
callback({ result: 'success', version: result.newMetadataVersion, metadata: metadata });
|
||||
@ -780,7 +875,17 @@ export async function startApi() {
|
||||
}
|
||||
|
||||
// Emit update to connected sockets
|
||||
pubsub.emit('update', userId, result.update);
|
||||
emitUpdateToInterestedClients({
|
||||
event: 'update',
|
||||
userId,
|
||||
sessionId: sid,
|
||||
payload: {
|
||||
id: result.update.id,
|
||||
seq: result.update.seq,
|
||||
body: result.update.content,
|
||||
createdAt: result.update.createdAt.getTime()
|
||||
}
|
||||
});
|
||||
|
||||
// Send success response with new version via callback
|
||||
callback({ result: 'success', version: result.newAgentStateVersion, agentState: agentState });
|
||||
|
91
yarn.lock
91
yarn.lock
@ -1091,6 +1091,11 @@ color-name@~1.1.4:
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
colorette@^2.0.7:
|
||||
version "2.0.20"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
|
||||
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
|
||||
|
||||
combined-stream@^1.0.8:
|
||||
version "1.0.8"
|
||||
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
|
||||
@ -1140,6 +1145,11 @@ date-fns@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-4.1.0.tgz#64b3d83fff5aa80438f5b1a633c2e83b8a1c2d14"
|
||||
integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==
|
||||
|
||||
dateformat@^4.6.3:
|
||||
version "4.6.3"
|
||||
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-4.6.3.tgz#556fa6497e5217fedb78821424f8a1c22fa3f4b5"
|
||||
integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==
|
||||
|
||||
debug@^4.1.1, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
@ -1222,6 +1232,13 @@ elevenlabs@^1.54.0:
|
||||
readable-stream "^4.5.2"
|
||||
url-join "4.0.1"
|
||||
|
||||
end-of-stream@^1.1.0:
|
||||
version "1.4.5"
|
||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.5.tgz#7344d711dea40e0b74abc2ed49778743ccedb08c"
|
||||
integrity sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==
|
||||
dependencies:
|
||||
once "^1.4.0"
|
||||
|
||||
engine.io-parser@~5.2.1:
|
||||
version "5.2.3"
|
||||
resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.3.tgz#00dc5b97b1f233a23c9398d0209504cf5f94d92f"
|
||||
@ -1362,6 +1379,11 @@ expect-type@^1.2.1:
|
||||
resolved "https://registry.yarnpkg.com/expect-type/-/expect-type-1.2.1.tgz#af76d8b357cf5fa76c41c09dafb79c549e75f71f"
|
||||
integrity sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==
|
||||
|
||||
fast-copy@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-copy/-/fast-copy-3.0.2.tgz#59c68f59ccbcac82050ba992e0d5c389097c9d35"
|
||||
integrity sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==
|
||||
|
||||
fast-decode-uri-component@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543"
|
||||
@ -1396,6 +1418,11 @@ fast-redact@^3.1.1:
|
||||
resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.5.0.tgz#e9ea02f7e57d0cd8438180083e93077e496285e4"
|
||||
integrity sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==
|
||||
|
||||
fast-safe-stringify@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
|
||||
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
|
||||
|
||||
fast-uri@^3.0.0, fast-uri@^3.0.1:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.6.tgz#88f130b77cfaea2378d56bf970dea21257a68748"
|
||||
@ -1553,6 +1580,11 @@ hasown@^2.0.2:
|
||||
dependencies:
|
||||
function-bind "^1.1.2"
|
||||
|
||||
help-me@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6"
|
||||
integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==
|
||||
|
||||
human-signals@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
|
||||
@ -1603,6 +1635,11 @@ jose@^6.0.11:
|
||||
resolved "https://registry.yarnpkg.com/jose/-/jose-6.0.11.tgz#0b7ea8b3b21a1bda5e00255a044c3a0e43270882"
|
||||
integrity sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==
|
||||
|
||||
joycon@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
|
||||
integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
|
||||
|
||||
json-schema-ref-resolver@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-schema-ref-resolver/-/json-schema-ref-resolver-2.0.1.tgz#c92f16b452df069daac53e1984159e0f9af0598d"
|
||||
@ -1753,6 +1790,11 @@ mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
minimist@^1.2.6:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
|
||||
mnemonist@0.40.0:
|
||||
version "0.40.0"
|
||||
resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.40.0.tgz#72e866d7f1e261d0c589717ff2bcfd6feb802db2"
|
||||
@ -1814,6 +1856,13 @@ on-exit-leak-free@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8"
|
||||
integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==
|
||||
|
||||
once@^1.3.1, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
onetime@^5.1.2:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
|
||||
@ -1853,6 +1902,25 @@ pino-abstract-transport@^2.0.0:
|
||||
dependencies:
|
||||
split2 "^4.0.0"
|
||||
|
||||
pino-pretty@^13.0.0:
|
||||
version "13.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-13.0.0.tgz#21d57fe940e34f2e279905d7dba2d7e2c4f9bf17"
|
||||
integrity sha512-cQBBIVG3YajgoUjo1FdKVRX6t9XPxwB9lcNJVD5GCnNM4Y6T12YYx8c6zEejxQsU0wrg9TwmDulcE9LR7qcJqA==
|
||||
dependencies:
|
||||
colorette "^2.0.7"
|
||||
dateformat "^4.6.3"
|
||||
fast-copy "^3.0.2"
|
||||
fast-safe-stringify "^2.1.1"
|
||||
help-me "^5.0.0"
|
||||
joycon "^3.1.1"
|
||||
minimist "^1.2.6"
|
||||
on-exit-leak-free "^2.1.0"
|
||||
pino-abstract-transport "^2.0.0"
|
||||
pump "^3.0.0"
|
||||
secure-json-parse "^2.4.0"
|
||||
sonic-boom "^4.0.1"
|
||||
strip-json-comments "^3.1.1"
|
||||
|
||||
pino-std-serializers@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz#7c625038b13718dbbd84ab446bd673dc52259e3b"
|
||||
@ -1932,6 +2000,14 @@ proxy-from-env@^1.1.0:
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.3.tgz#151d979f1a29668dc0025ec589a455b53282268d"
|
||||
integrity sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
pvtsutils@^1.3.5, pvtsutils@^1.3.6:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.6.tgz#ec46e34db7422b9e4fdc5490578c1883657d6001"
|
||||
@ -2060,6 +2136,11 @@ safe-stable-stringify@^2.3.1:
|
||||
resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd"
|
||||
integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==
|
||||
|
||||
secure-json-parse@^2.4.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz#5a5f9cd6ae47df23dba3151edd06855d47e09862"
|
||||
integrity sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==
|
||||
|
||||
secure-json-parse@^3.0.1:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/secure-json-parse/-/secure-json-parse-3.0.2.tgz#255b03bb0627ba5805f64f384b0a7691d8cb021b"
|
||||
@ -2217,6 +2298,11 @@ strip-final-newline@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
|
||||
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
|
||||
|
||||
strip-json-comments@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
supports-color@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
@ -2461,6 +2547,11 @@ why-is-node-running@^2.3.0:
|
||||
siginfo "^2.0.0"
|
||||
stackback "0.0.2"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
ws@~8.17.1:
|
||||
version "8.17.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
|
||||
|
Loading…
Reference in New Issue
Block a user