diff --git a/.gitignore b/.gitignore index 1f5a509..4011990 100644 --- a/.gitignore +++ b/.gitignore @@ -31,5 +31,3 @@ project.org apps/bridge-worker/scripts/* ENVIRONMENT_VARIABLES_MIGRATION.md local-scripts/* -docs/ -packages/zammad-addon-bridge/test/ diff --git a/.nvmrc b/.nvmrc index 54c6511..32cfab6 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v24 +v22.18.0 diff --git a/apps/bridge-frontend/package.json b/apps/bridge-frontend/package.json index 27c4a82..028641a 100644 --- a/apps/bridge-frontend/package.json +++ b/apps/bridge-frontend/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/bridge-frontend", - "version": "3.5.0-beta.1", + "version": "3.3.5", "type": "module", "scripts": { "dev": "next dev", diff --git a/apps/bridge-migrations/package.json b/apps/bridge-migrations/package.json index 3c23b1c..b6740ce 100644 --- a/apps/bridge-migrations/package.json +++ b/apps/bridge-migrations/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/bridge-migrations", - "version": "3.5.0-beta.1", + "version": "3.3.5", "type": "module", "scripts": { "migrate:up:all": "tsx migrate.ts up:all", diff --git a/apps/bridge-whatsapp/package.json b/apps/bridge-whatsapp/package.json index 97ea951..cef6520 100644 --- a/apps/bridge-whatsapp/package.json +++ b/apps/bridge-whatsapp/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/bridge-whatsapp", - "version": "3.5.0-beta.1", + "version": "3.3.5", "main": "build/main/index.js", "author": "Darren Clarke ", "license": "AGPL-3.0-or-later", diff --git a/apps/bridge-whatsapp/src/index.ts b/apps/bridge-whatsapp/src/index.ts index bd255ff..5be8763 100644 --- a/apps/bridge-whatsapp/src/index.ts +++ b/apps/bridge-whatsapp/src/index.ts @@ -1,17 +1,17 @@ import * as Hapi from "@hapi/hapi"; import hapiPino from "hapi-pino"; import Schmervice from "@hapipal/schmervice"; -import WhatsappService from "./service.ts"; +import WhatsappService from "./service.js"; import { RegisterBotRoute, UnverifyBotRoute, GetBotRoute, SendMessageRoute, ReceiveMessageRoute, -} from "./routes.ts"; +} from "./routes.js"; import { createLogger } from "@link-stack/logger"; -const logger = createLogger("bridge-whatsapp-index"); +const logger = createLogger('bridge-whatsapp-index'); const server = Hapi.server({ port: 5000 }); diff --git a/apps/bridge-whatsapp/src/routes.ts b/apps/bridge-whatsapp/src/routes.ts index 7315020..1d9caf6 100644 --- a/apps/bridge-whatsapp/src/routes.ts +++ b/apps/bridge-whatsapp/src/routes.ts @@ -1,6 +1,6 @@ import * as Hapi from "@hapi/hapi"; import Toys from "@hapipal/toys"; -import WhatsappService from "./service.ts"; +import WhatsappService from "./service"; const withDefaults = Toys.withRouteDefaults({ options: { @@ -27,9 +27,15 @@ export const SendMessageRoute = withDefaults({ description: "Send a message", async handler(request: Hapi.Request, _h: Hapi.ResponseToolkit) { const { id } = request.params; - const { phoneNumber, message, attachments } = request.payload as MessageRequest; + const { phoneNumber, message, attachments } = + request.payload as MessageRequest; const whatsappService = getService(request); - await whatsappService.send(id, phoneNumber, message as string, attachments); + await whatsappService.send( + id, + phoneNumber, + message as string, + attachments, + ); request.logger.info( { id, diff --git a/apps/bridge-whatsapp/src/service.ts b/apps/bridge-whatsapp/src/service.ts index 43bc205..69a1895 100644 --- a/apps/bridge-whatsapp/src/service.ts +++ b/apps/bridge-whatsapp/src/service.ts @@ -4,13 +4,12 @@ import makeWASocket, { DisconnectReason, proto, downloadContentFromMessage, + MediaType, fetchLatestBaileysVersion, isJidBroadcast, isJidStatusBroadcast, useMultiFileAuthState, } from "@whiskeysockets/baileys"; - -type MediaType = "audio" | "document" | "image" | "video" | "sticker"; import fs from "fs"; import { createLogger } from "@link-stack/logger"; import { @@ -98,7 +97,6 @@ export default class WhatsappService extends Service { ...options, auth: state, generateHighQualityLinkPreview: false, - syncFullHistory: true, msgRetryCounterMap, shouldIgnoreJid: (jid) => isJidBroadcast(jid) || isJidStatusBroadcast(jid), }); @@ -149,17 +147,6 @@ export default class WhatsappService extends Service { await this.queueUnreadMessages(botID, messages); } } - - if (events["messaging-history.set"]) { - const { messages, isLatest } = events["messaging-history.set"]; - logger.info( - { messageCount: messages.length, isLatest }, - "received message history on connection", - ); - if (messages.length > 0) { - await this.queueUnreadMessages(botID, messages); - } - } }); this.connections[botID] = { socket, msgRetryCounterMap }; @@ -180,6 +167,7 @@ export default class WhatsappService extends Service { await this.createConnection(botID, this.server, { browser: WhatsappService.browserDescription, + printQRInTerminal: true, version, }); } @@ -187,20 +175,18 @@ export default class WhatsappService extends Service { } private async queueMessage(botID: string, webMessageInfo: proto.IWebMessageInfo) { - const { key, message, messageTimestamp } = webMessageInfo; - if (!key) { - logger.warn("Message missing key, skipping"); - return; + const { + key: { id, fromMe, remoteJid }, + message, + messageTimestamp, + } = webMessageInfo; + logger.info("Message type debug"); + for (const key in message) { + logger.info( + { key, exists: !!message[key as keyof proto.IMessage] }, + "Message field", + ); } - const { id, fromMe, remoteJid } = key; - // Baileys 7 uses LIDs (Linked IDs) instead of phone numbers in some cases. - // senderPn contains the actual phone number when available. - const senderPn = (key as any).senderPn as string | undefined; - const participantPn = (key as any).participantPn as string | undefined; - logger.info( - { remoteJid, senderPn, participantPn, fromMe }, - "Processing incoming message", - ); const isValidMessage = message && remoteJid !== "status@broadcast" && !fromMe; if (isValidMessage) { const { audioMessage, documentMessage, imageMessage, videoMessage } = message; @@ -258,27 +244,9 @@ export default class WhatsappService extends Service { videoMessage, ].find((text) => text && text !== ""); - // Extract phone number and user ID (LID) separately - // remoteJid may contain LIDs (Baileys 7+) which are not phone numbers - const jidValue = remoteJid?.split("@")[0]; - const isLidJid = remoteJid?.endsWith("@lid"); - - // Phone number: prefer senderPn/participantPn, fall back to remoteJid only if it's not a LID - const senderPhone = senderPn?.split("@")[0] || participantPn?.split("@")[0] || (!isLidJid ? jidValue : undefined); - - // User ID (LID): extract from remoteJid if it's a LID format - const senderUserId = isLidJid ? jidValue : undefined; - - // Must have at least one identifier - if (!senderPhone && !senderUserId) { - logger.warn({ remoteJid, senderPn, participantPn }, "Could not determine sender identity, skipping message"); - return; - } - const payload = { to: botID, - from: senderPhone, - userId: senderUserId, + from: remoteJid?.split("@")[0], messageId: id, sentAt: new Date((messageTimestamp as number) * 1000).toISOString(), message: messageText, @@ -442,17 +410,12 @@ export default class WhatsappService extends Service { } async receive( - _botID: string, + botID: string, _lastReceivedDate: Date, ): Promise { - // loadAllUnreadMessages() was removed in Baileys 7.x - // Messages are now delivered via events (messages.upsert, messaging-history.set) - // and forwarded to webhooks automatically. - // See: https://baileys.wiki/docs/migration/to-v7.0.0/ - throw new Error( - "Message polling is no longer supported in Baileys 7.x. " + - "Please configure a webhook to receive messages instead. " + - "Messages are automatically forwarded to BRIDGE_FRONTEND_URL/api/whatsapp/bots/{id}/receive" - ); + const connection = this.connections[botID]?.socket; + const messages = await connection.loadAllUnreadMessages(); + + return messages; } } diff --git a/apps/bridge-whatsapp/src/types.ts b/apps/bridge-whatsapp/src/types.ts index dc6d4fb..231a804 100644 --- a/apps/bridge-whatsapp/src/types.ts +++ b/apps/bridge-whatsapp/src/types.ts @@ -1,4 +1,4 @@ -import type WhatsappService from "./service.ts"; +import type WhatsappService from "./service.js"; declare module "@hapipal/schmervice" { interface SchmerviceDecorator { diff --git a/apps/bridge-whatsapp/tsconfig.json b/apps/bridge-whatsapp/tsconfig.json index dce4015..4b714ce 100644 --- a/apps/bridge-whatsapp/tsconfig.json +++ b/apps/bridge-whatsapp/tsconfig.json @@ -1,17 +1,16 @@ { "extends": "@link-stack/typescript-config/tsconfig.node.json", "compilerOptions": { - "module": "NodeNext", - "target": "es2022", + "module": "commonjs", + "target": "es2018", "esModuleInterop": true, - "moduleResolution": "NodeNext", + "moduleResolution": "node", "outDir": "build/main", "rootDir": "src", "skipLibCheck": true, "types": ["node"], - "lib": ["es2022", "DOM"], - "composite": true, - "rewriteRelativeImportExtensions": true + "lib": ["es2020", "DOM"], + "composite": true }, "include": ["src/**/*.ts", "src/**/.*.ts"], "exclude": ["node_modules/**"] diff --git a/apps/bridge-worker/package.json b/apps/bridge-worker/package.json index 7d7e8ff..670d742 100644 --- a/apps/bridge-worker/package.json +++ b/apps/bridge-worker/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/bridge-worker", - "version": "3.5.0-beta.1", + "version": "3.3.5", "type": "module", "main": "build/main/index.js", "author": "Darren Clarke ", diff --git a/apps/bridge-worker/tasks/fetch-signal-messages.ts b/apps/bridge-worker/tasks/fetch-signal-messages.ts index af6c80c..f4bac04 100644 --- a/apps/bridge-worker/tasks/fetch-signal-messages.ts +++ b/apps/bridge-worker/tasks/fetch-signal-messages.ts @@ -168,7 +168,6 @@ const processMessage = async ({ token: id, to: toRecipient, from: source, - userId: sourceUuid, // Signal user UUID for user identification messageId: `${sourceUuid}-${rawTimestamp}`, message: dataMessage?.message, sentAt: timestamp.toISOString(), @@ -205,16 +204,17 @@ const fetchSignalMessagesTask = async ({ if (scheduleTasks === "true") { // because cron only has minimum 1 minute resolution - // schedule one additional job at 30s to achieve 30-second polling - await worker.addJob( - "fetch-signal-messages", - { scheduleTasks: "false" }, - { - maxAttempts: 1, - runAt: new Date(Date.now() + 30000), - jobKey: "fetchSignalMessages-30000", - }, - ); + for (const offset of [15000, 30000, 45000]) { + await worker.addJob( + "fetch-signal-messages", + { scheduleTasks: "false" }, + { + maxAttempts: 1, + runAt: new Date(Date.now() + offset), + jobKey: `fetchSignalMessages-${offset}`, + }, + ); + } } const messagesClient = new MessagesApi(config); diff --git a/apps/bridge-worker/tasks/signal/receive-signal-message.ts b/apps/bridge-worker/tasks/signal/receive-signal-message.ts index 331f41a..a504b3c 100644 --- a/apps/bridge-worker/tasks/signal/receive-signal-message.ts +++ b/apps/bridge-worker/tasks/signal/receive-signal-message.ts @@ -9,7 +9,6 @@ interface ReceiveSignalMessageTaskOptions { token: string; to: string; from: string; - userId?: string; // Signal user UUID for user identification messageId: string; sentAt: string; message: string; @@ -23,7 +22,6 @@ const receiveSignalMessageTask = async ({ token, to, from, - userId, messageId, sentAt, message, @@ -214,7 +212,6 @@ const receiveSignalMessageTask = async ({ const payload = { to: finalTo, from, - user_id: userId, // Signal user UUID for user identification message_id: messageId, sent_at: sentAt, message, diff --git a/apps/bridge-worker/tasks/signal/send-signal-message.ts b/apps/bridge-worker/tasks/signal/send-signal-message.ts index 6c339f3..3235be2 100644 --- a/apps/bridge-worker/tasks/signal/send-signal-message.ts +++ b/apps/bridge-worker/tasks/signal/send-signal-message.ts @@ -64,14 +64,13 @@ const sendSignalMessageTask = async ({ let groupCreated = false; try { - // Check if 'to' is a group ID (group.base64 format or base64 internal ID) vs individual recipient - // Signal group IDs are 32 bytes = 44 chars base64 (or 43 without padding) - // Signal user UUIDs (ACIs) are 36 chars with hyphens: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx - // Phone numbers start with +, usernames with u:, PNIs with PNI: + // Check if 'to' is a group ID (UUID format, group.base64 format, or base64) vs phone number + const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test( + to, + ); const isGroupPrefix = to.startsWith("group."); - const isBase64GroupId = - /^[A-Za-z0-9+/]+=*$/.test(to) && to.length >= 43 && to.length <= 44; - const isGroupId = isGroupPrefix || isBase64GroupId; + const isBase64 = /^[A-Za-z0-9+/]+=*$/.test(to) && to.length > 20; // Base64 internal_id + const isGroupId = isUUID || isGroupPrefix || isBase64; const enableAutoGroups = process.env.BRIDGE_SIGNAL_AUTO_GROUPS === "true"; logger.debug( @@ -283,35 +282,6 @@ const sendSignalMessageTask = async ({ }, "Message sent successfully", ); - - // Update group name to use consistent template with ticket number - // This ensures groups created by receive-signal-message get renamed - // to match the template (e.g., "Support Request: 94085") - if (finalTo.startsWith("group.") && conversationId) { - try { - const expectedGroupName = buildSignalGroupName(conversationId); - await groupsClient.v1GroupsNumberGroupidPut({ - number: bot.phoneNumber, - groupid: finalTo, - data: { - name: expectedGroupName, - }, - }); - logger.debug( - { groupId: finalTo, newName: expectedGroupName }, - "Updated group name", - ); - } catch (renameError) { - // Non-fatal - group name update is best-effort - logger.warn( - { - error: renameError instanceof Error ? renameError.message : renameError, - groupId: finalTo, - }, - "Could not update group name", - ); - } - } } catch (error: any) { // Try to get the actual error message from the response if (error.response) { diff --git a/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts b/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts index 94cee82..48adc6f 100644 --- a/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts +++ b/apps/bridge-worker/tasks/whatsapp/receive-whatsapp-message.ts @@ -3,8 +3,7 @@ import { db, getWorkerUtils } from "@link-stack/bridge-common"; interface ReceiveWhatsappMessageTaskOptions { token: string; to: string; - from?: string; - userId?: string; + from: string; messageId: string; sentAt: string; message: string; @@ -17,7 +16,6 @@ const receiveWhatsappMessageTask = async ({ token, to, from, - userId, messageId, sentAt, message, @@ -35,7 +33,6 @@ const receiveWhatsappMessageTask = async ({ const payload = { to, from, - user_id: userId, message_id: messageId, sent_at: sentAt, message, diff --git a/apps/link/Dockerfile b/apps/link/Dockerfile index 98523f4..689fffe 100644 --- a/apps/link/Dockerfile +++ b/apps/link/Dockerfile @@ -42,11 +42,10 @@ RUN corepack enable && corepack prepare pnpm@9.15.4 --activate RUN DEBIAN_FRONTEND=noninteractive apt-get update && \ apt-get install -y --no-install-recommends \ dumb-init -RUN mkdir -p ${APP_DIR} /pnpm -RUN chown -R node ${APP_DIR}/ /pnpm +RUN mkdir -p ${APP_DIR} +RUN chown -R node ${APP_DIR}/ USER node -RUN corepack prepare pnpm@9.15.4 --activate WORKDIR ${APP_DIR} COPY --from=installer ${APP_DIR} ./ USER root diff --git a/apps/link/app/(login)/login/_components/Login.tsx b/apps/link/app/(login)/login/_components/Login.tsx index 0df2147..3207dfd 100644 --- a/apps/link/app/(login)/login/_components/Login.tsx +++ b/apps/link/app/(login)/login/_components/Login.tsx @@ -14,7 +14,6 @@ import { Google as GoogleIcon, Microsoft as MicrosoftIcon, Key as KeyIcon, - VpnKey as KeycloakIcon, } from "@mui/icons-material"; import { signIn, getProviders } from "next-auth/react"; import Image from "next/image"; @@ -201,21 +200,6 @@ export const Login: FC = ({ session, baseURL }) => { )} - {provider === "keycloak" && ( - - - signIn("keycloak", { - callbackUrl, - }) - } - > - - Sign in with Keycloak - - - )} {provider === "credentials" && ( diff --git a/apps/link/app/_lib/authentication.ts b/apps/link/app/_lib/authentication.ts index 49f2e07..6370038 100644 --- a/apps/link/app/_lib/authentication.ts +++ b/apps/link/app/_lib/authentication.ts @@ -11,7 +11,6 @@ import Google from "next-auth/providers/google"; import Credentials from "next-auth/providers/credentials"; import Apple from "next-auth/providers/apple"; import AzureADProvider from "next-auth/providers/azure-ad"; -import Keycloak from "next-auth/providers/keycloak"; import { createLogger } from "@link-stack/logger"; const logger = createLogger('link-authentication'); @@ -102,18 +101,6 @@ if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { tenantId: process.env.AZURE_AD_TENANT_ID, }), ); -} else if ( - process.env.KEYCLOAK_CLIENT_ID && - process.env.KEYCLOAK_CLIENT_SECRET && - process.env.KEYCLOAK_ISSUER -) { - providers.push( - Keycloak({ - clientId: process.env.KEYCLOAK_CLIENT_ID, - clientSecret: process.env.KEYCLOAK_CLIENT_SECRET, - issuer: process.env.KEYCLOAK_ISSUER, - }), - ); } else { providers.push( Credentials({ diff --git a/apps/link/package.json b/apps/link/package.json index 45e61a3..1f15196 100644 --- a/apps/link/package.json +++ b/apps/link/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/link", - "version": "3.5.0-beta.1", + "version": "3.3.5", "type": "module", "scripts": { "dev": "next dev -H 0.0.0.0", diff --git a/docker/buildx/Dockerfile b/docker/buildx/Dockerfile index 28679d2..dc394b5 100644 --- a/docker/buildx/Dockerfile +++ b/docker/buildx/Dockerfile @@ -15,7 +15,7 @@ COPY --from=node /usr/local/lib /usr/local/lib COPY --from=node /usr/local/include /usr/local/include COPY --from=node /usr/local/bin /usr/local/bin -# Prepare pnpm (corepack symlinks already copied from node image) +# Prepare pnpm (corepack is already enabled in node:22-alpine) RUN corepack prepare pnpm@9.15.4 --activate # Set up pnpm home diff --git a/docker/scripts/docker.js b/docker/scripts/docker.js index 7acf49a..3d6e278 100644 --- a/docker/scripts/docker.js +++ b/docker/scripts/docker.js @@ -5,13 +5,13 @@ const app = process.argv[2]; const command = process.argv[3]; const files = { - all: ["zammad", "postgresql", "bridge", "opensearch", "link", "signal-cli-rest-api", "bridge-whatsapp"], + all: ["zammad", "postgresql", "bridge", "opensearch", "link", "signal-cli-rest-api"], linkDev: ["zammad", "postgresql", "opensearch"], link: ["zammad", "postgresql", "opensearch", "link"], linkOnly: ["link"], opensearch: ["opensearch"], - bridgeDev: ["zammad", "postgresql", "signal-cli-rest-api", "bridge-whatsapp"], - bridge: ["zammad", "postgresql", "bridge", "signal-cli-rest-api", "bridge-whatsapp"], + bridgeDev: ["zammad", "postgresql", "signal-cli-rest-api"], + bridge: ["zammad", "postgresql", "bridge", "signal-cli-rest-api"], zammad: ["zammad", "postgresql", "opensearch"], }; @@ -21,12 +21,11 @@ const finalFiles = files[app] .map((file) => ['-f', `docker/compose/${file}.yml`]).flat(); // Add bridge-dev.yml for dev commands that include zammad -const devAppsWithZammad = ['linkDev', 'bridgeDev']; +const devAppsWithZammad = ['linkDev', 'bridgeDev', 'all']; if (devAppsWithZammad.includes(app) && files[app].includes('zammad')) { finalFiles.push('-f', 'docker-compose.bridge-dev.yml'); } - const finalCommand = command === "up" ? ["up", "-d", "--remove-orphans"] : [command]; const dockerCompose = spawn('docker', ['compose', '--env-file', envFile, ...finalFiles, ...finalCommand]); diff --git a/docker/zammad/Dockerfile b/docker/zammad/Dockerfile index dc7bf5a..018304d 100644 --- a/docker/zammad/Dockerfile +++ b/docker/zammad/Dockerfile @@ -66,7 +66,7 @@ RUN if [ "$EMBEDDED" = "true" ] ; then \ sed -i '$ d' /opt/zammad/contrib/nginx/zammad.conf && \ echo "" >> /opt/zammad/contrib/nginx/zammad.conf && \ echo " location /link {" >> /opt/zammad/contrib/nginx/zammad.conf && \ - echo " set \$link_url ${LINK_HOST}; proxy_pass \$link_url;" >> /opt/zammad/contrib/nginx/zammad.conf && \ + echo " proxy_pass ${LINK_HOST};" >> /opt/zammad/contrib/nginx/zammad.conf && \ echo " proxy_set_header Host \$host;" >> /opt/zammad/contrib/nginx/zammad.conf && \ echo " proxy_set_header X-Real-IP \$remote_addr;" >> /opt/zammad/contrib/nginx/zammad.conf && \ echo " proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;" >> /opt/zammad/contrib/nginx/zammad.conf && \ diff --git a/docs/bridge-ticket-split-merge-investigation.md b/docs/bridge-ticket-split-merge-investigation.md deleted file mode 100644 index ebf48b3..0000000 --- a/docs/bridge-ticket-split-merge-investigation.md +++ /dev/null @@ -1,506 +0,0 @@ -# Zammad Ticket Splits & Merges with Bridge Channels - -## Investigation Summary - -This document analyzes how Zammad handles ticket splits and merges, and the implications for our custom bridge channels (WhatsApp, Signal, Voice). - -## Current State - -### How Zammad Handles Split/Merge (Built-in) - -#### Merge (`ticket.merge_to` in `app/models/ticket.rb:330`) - -When ticket A is merged into ticket B: - -1. All articles from A are moved to B -2. A "parent" link is created between A and B -3. Mentions and external links are migrated -4. Source ticket A's state is set to "merged" -5. Source ticket A's owner is reset to System (id: 1) - -**Critical issue:** Ticket preferences are NOT copied or migrated. The target ticket B keeps its original preferences, and source ticket A's preferences become orphaned. - -```ruby -# From app/models/ticket.rb - merge_to method -# Articles are moved: -Ticket::Article.where(ticket_id: id).update_all(['ticket_id = ?', data[:ticket_id]]) - -# But preferences are never touched - they stay on the source ticket -``` - -#### Split (`app/models/form_updater/concerns/applies_split_ticket_article.rb`) - -When an article is split from ticket A to create new ticket C: - -1. Basic ticket attributes are copied (group, customer, state, priority, title) -2. Attachments are cloned -3. A link is created to the original ticket -4. `owner_id` is explicitly deleted (not copied) - -**Critical issue:** Preferences are NOT copied. The new ticket C has no channel metadata. - -```ruby -# From applies_split_ticket_article.rb -def attributes_to_apply - attrs = selected_ticket_article.ticket.attributes - attrs['title'] = selected_ticket_article.subject if selected_ticket_article.subject.present? - attrs['body'] = body_with_form_id_urls - attrs.delete 'owner_id' # Explicitly deleted - attrs - # Note: preferences are NOT included in .attributes -end -``` - -#### Email Follow-up Handling (`app/models/channel/filter/follow_up_merged.rb`) - -Zammad has a postmaster filter that handles incoming emails to merged tickets: - -```ruby -def self.run(_channel, mail, _transaction_params) - return if mail[:'x-zammad-ticket-id'].blank? - - referenced_ticket = Ticket.find_by(id: mail[:'x-zammad-ticket-id']) - return if referenced_ticket.blank? - - new_target_ticket = find_merge_follow_up_ticket(referenced_ticket) - return if new_target_ticket.blank? - - mail[:'x-zammad-ticket-id'] = new_target_ticket.id -end -``` - -This follows the parent link to find the active target ticket. **This only works for email** - no equivalent exists for other channels like Telegram, WhatsApp, or Signal. - ---- - -## Bridge Channel Metadata Structure - -Our bridge channels store critical routing metadata in `ticket.preferences`: - -### WhatsApp - -```ruby -ticket.preferences = { - channel_id: 123, - cdr_whatsapp: { - bot_token: "abc123", # Identifies which bot/channel - chat_id: "+1234567890" # Customer's phone number - WHERE TO SEND - } -} -``` - -### Signal (Direct Message) - -```ruby -ticket.preferences = { - channel_id: 456, - cdr_signal: { - bot_token: "xyz789", - chat_id: "+1234567890" # Customer's phone number - } -} -``` - -### Signal (Group) - -```ruby -ticket.preferences = { - channel_id: 456, - cdr_signal: { - bot_token: "xyz789", - chat_id: "group.abc123...", # Signal group ID - group_joined: true, # Whether customer accepted invite - group_joined_at: "2024-01-01", - original_recipient: "+1234567890" - } -} -``` - ---- - -## How Bridge Channels Use This Metadata - -### Outgoing Messages - -The communication jobs (`CommunicateCdrWhatsappJob`, `CommunicateCdrSignalJob`) rely entirely on ticket preferences: - -```ruby -# From communicate_cdr_whatsapp_job.rb -def perform(article_id) - article = Ticket::Article.find(article_id) - ticket = Ticket.lookup(id: article.ticket_id) - - # These MUST exist or the job fails: - unless ticket.preferences['cdr_whatsapp']['bot_token'] - log_error(article, "Can't find ticket.preferences['cdr_whatsapp']['bot_token']") - end - unless ticket.preferences['cdr_whatsapp']['chat_id'] - log_error(article, "Can't find ticket.preferences['cdr_whatsapp']['chat_id']") - end - - channel = Channel.lookup(id: ticket.preferences['channel_id']) - result = channel.deliver(article) # Uses chat_id to know where to send -end -``` - -### Incoming Messages - -The webhook controllers look up existing tickets: - -**WhatsApp** (`channels_cdr_whatsapp_controller.rb`): -```ruby -# Find open ticket for this customer -state_ids = Ticket::State.where(name: %w[closed merged removed]).pluck(:id) -ticket = Ticket.where(customer_id: customer.id) - .where.not(state_id: state_ids) - .order(:updated_at).first -``` - -**Signal Groups** (`channels_cdr_signal_controller.rb`): -```ruby -# Find ticket by group ID in preferences -ticket = Ticket.where.not(state_id: state_ids) - .where("preferences LIKE ?", "%channel_id: #{channel.id}%") - .where("preferences LIKE ?", "%chat_id: #{receiver_phone_number}%") - .order(updated_at: :desc) - .first -``` - ---- - -## Problem Scenarios - -### Scenario 1: Merge Bridge Ticket → Non-Bridge Ticket - -**Setup:** Ticket A (has WhatsApp metadata) merged into Ticket B (no bridge metadata) - -**What happens:** -- A's articles move to B -- A's preferences stay on A (now in merged state) -- B still has no bridge preferences - -**Result:** Agent replies on ticket B fail - no `chat_id` to send to. - -### Scenario 2: Merge Bridge Ticket → Different Bridge Ticket - -**Setup:** Ticket A (WhatsApp to +111) merged into Ticket B (WhatsApp to +222) - -**What happens:** -- A's articles move to B -- B keeps its preferences (`chat_id: +222`) - -**Result:** Agent replies go to +222, not to +111. Customer +111 never receives responses. - -### Scenario 3: Split Article from Bridge Ticket - -**Setup:** Split an article from Ticket A (has WhatsApp metadata) to create Ticket C - -**What happens:** -- New ticket C is created with no preferences -- C is linked to A - -**Result:** Agent cannot reply via WhatsApp on ticket C at all - job fails immediately. - -### Scenario 4: Incoming Message to Merged Ticket's Customer - -**Setup:** Ticket A (customer +111) was merged into B. Customer +111 sends new message. - -**What happens:** -- Webhook finds customer by phone number -- Looks for open ticket for customer -- A is excluded (merged state) -- Either finds B (if same customer) or creates new ticket - -**Result:** May work if B has same customer, but conversation context is fragmented. - -### Scenario 5: Signal Group Ticket Merged - -**Setup:** Ticket A (Signal group X) merged into Ticket B (no Signal metadata) - -**What happens:** -- All group messages went to A -- A is now merged, B has no group reference -- New messages from group X create a new ticket (can't find existing by group ID) - -**Result:** Conversation splits into multiple tickets unexpectedly. - ---- - -## Recommended Solutions - -### Option 1: Preferences Migration on Merge (Recommended) - -Create a concern that copies bridge channel metadata when tickets are merged: - -```ruby -# app/models/ticket/merge_bridge_channel_preferences.rb -module Ticket::MergeBridgeChannelPreferences - extend ActiveSupport::Concern - - included do - after_update :migrate_bridge_preferences_on_merge - end - - private - - def migrate_bridge_preferences_on_merge - return unless saved_change_to_state_id? - return unless state.state_type.name == 'merged' - - target_ticket = find_merge_target - return unless target_ticket - - # Copy bridge preferences if target doesn't have them - %w[cdr_whatsapp cdr_signal cdr_voice].each do |channel_key| - next unless preferences[channel_key].present? - next if target_ticket.preferences[channel_key].present? - - target_ticket.preferences[channel_key] = preferences[channel_key].deep_dup - target_ticket.preferences['channel_id'] ||= preferences['channel_id'] - end - - target_ticket.save! if target_ticket.changed? - end - - def find_merge_target - Link.list(link_object: 'Ticket', link_object_value: id) - .find { |l| l['link_type'] == 'parent' && l['link_object'] == 'Ticket' } - &.then { |l| Ticket.find_by(id: l['link_object_value']) } - end -end -``` - -**Pros:** -- Handles the common case (merging bridge ticket into non-bridge ticket) -- Automatic, no agent action required -- Non-destructive (doesn't overwrite existing preferences) - -**Cons:** -- Doesn't handle case where both tickets have different bridge metadata -- May need additional logic for conflicting preferences - -### Option 2: Follow-up Filter for Bridge Channels - -Create filters similar to `FollowUpMerged` that redirect incoming bridge messages: - -```ruby -# Modify webhook controllers to check for merged tickets -def find_active_ticket_for_customer(customer, state_ids) - ticket = Ticket.where(customer_id: customer.id) - .where.not(state_id: state_ids) - .order(:updated_at).first - - # If ticket is merged, follow parent link - if ticket&.state&.state_type&.name == 'merged' - ticket = find_merge_target(ticket) || ticket - end - - ticket -end - -def find_merge_target(ticket) - Link.list(link_object: 'Ticket', link_object_value: ticket.id) - .filter_map do |link| - next if link['link_type'] != 'parent' - next if link['link_object'] != 'Ticket' - - Ticket.joins(state: :state_type) - .where.not(ticket_state_types: { name: 'merged' }) - .find_by(id: link['link_object_value']) - end.first -end -``` - -**Pros:** -- Handles incoming messages to merged tickets correctly -- Follows same pattern as Zammad's email handling - -**Cons:** -- Requires modifying webhook controllers -- Only handles incoming direction, not outgoing - -### Option 3: Copy Preferences on Split - -Modify the split form updater or add a callback to copy bridge preferences: - -```ruby -# Add to ticket creation from split -module Ticket::SplitBridgeChannelPreferences - extend ActiveSupport::Concern - - included do - after_create :copy_bridge_preferences_from_source - end - - private - - def copy_bridge_preferences_from_source - # Find source ticket via link - source_link = Link.list(link_object: 'Ticket', link_object_value: id) - .find { |l| l['link_type'] == 'child' } - return unless source_link - - source_ticket = Ticket.find_by(id: source_link['link_object_value']) - return unless source_ticket - - # Copy bridge preferences - %w[cdr_whatsapp cdr_signal cdr_voice channel_id].each do |key| - next unless source_ticket.preferences[key].present? - self.preferences[key] = source_ticket.preferences[key].deep_dup - end - - save! if changed? - end -end -``` - -### Option 4: UI Warning + Manual Handling - -Add frontend validation to warn agents: - -1. Check for bridge preferences before merge/split -2. Show warning dialog explaining implications -3. Optionally provide UI to manually transfer channel association - -```typescript -// In merge confirmation dialog -const hasBridgeChannel = ticket.preferences?.cdr_whatsapp || - ticket.preferences?.cdr_signal; -if (hasBridgeChannel) { - showWarning( - "This ticket uses WhatsApp/Signal messaging. " + - "Merging may affect message routing. " + - "Replies will be sent to the target ticket's contact." - ); -} -``` - -### Option 5: Multi-Channel Preferences (Long-term) - -Allow tickets to have multiple channel associations: - -```ruby -ticket.preferences = { - bridge_channels: [ - { type: 'cdr_whatsapp', chat_id: '+111...', channel_id: 1, customer_id: 100 }, - { type: 'cdr_whatsapp', chat_id: '+222...', channel_id: 1, customer_id: 101 }, - { type: 'cdr_signal', chat_id: 'group.xxx', channel_id: 2 } - ] -} -``` - -This would require significant refactoring of communication jobs to handle multiple recipients. - ---- - -## Signal Groups - Special Considerations - -Signal groups add complexity: - -1. **Group ID is the routing key**, not phone number -2. **Multiple customers** might be in the same group -3. **`group_joined` flag** tracks invite acceptance - messages can't be sent until true -4. **Group membership changes** could affect ticket routing - -### Merge Rules for Signal Groups - -| Source Ticket | Target Ticket | Recommendation | -|---------------|---------------|----------------| -| Signal group A | No Signal | Copy preferences (Option 1) | -| Signal group A | Signal group A (same) | Safe to merge | -| Signal group A | Signal group B (different) | **Block or warn** - can't merge different group conversations | -| Signal group A | Signal DM | **Block or warn** - different communication modes | - -Consider adding validation: - -```ruby -def validate_signal_group_merge(source, target) - source_group = source.preferences.dig('cdr_signal', 'chat_id') - target_group = target.preferences.dig('cdr_signal', 'chat_id') - - return true if source_group.blank? || target_group.blank? - return true if source_group == target_group - - # Different groups - this is problematic - raise Exceptions::UnprocessableEntity, - "Cannot merge tickets from different Signal groups" -end -``` - ---- - -## Recommended Implementation Path - -### Phase 1: Immediate (Low Risk) - -1. **Add preferences migration on merge** (Option 1) - - Only copies if target doesn't have existing preferences - - Handles most common case safely - -2. **Add preferences copy on split** (Option 3) - - New tickets get parent's channel metadata - - Enables replies on split tickets - -### Phase 2: Short-term - -3. **Add follow-up handling in webhooks** (Option 2) - - Modify webhook controllers to follow merge parent links - - Handles incoming messages to merged ticket's customer - -4. **Add UI warnings** (Option 4) - - Warn agents about implications - - Especially for conflicting metadata scenarios - -### Phase 3: Medium-term - -5. **Add merge validation for Signal groups** - - Block merging tickets from different groups - - Or add clear warning about implications - -6. **Add audit logging** - - Track when preferences are migrated - - Help agents understand what happened - ---- - -## Files to Modify - -### Zammad Addon (zammad-addon-bridge) - -| File | Change | -|------|--------| -| `src/app/models/ticket/merge_bridge_channel_preferences.rb` | New - preferences migration | -| `src/app/models/ticket/split_bridge_channel_preferences.rb` | New - preferences copy on split | -| `src/app/controllers/channels_cdr_whatsapp_controller.rb` | Add merge follow-up handling | -| `src/app/controllers/channels_cdr_signal_controller.rb` | Add merge follow-up handling | -| `src/config/initializers/bridge.rb` | Include new concerns in Ticket model | - -### Link Frontend (optional) - -| File | Change | -|------|--------| -| Merge dialog component | Add warning for bridge tickets | - ---- - -## Testing Scenarios - -1. Merge WhatsApp ticket → empty ticket → verify agent can reply -2. Merge WhatsApp ticket → WhatsApp ticket (same number) → verify routing -3. Merge WhatsApp ticket → WhatsApp ticket (different number) → verify warning/behavior -4. Split article from WhatsApp ticket → verify new ticket has preferences -5. Customer sends message after their ticket was merged → verify routing -6. Merge Signal group ticket → verify group_joined flag is preserved -7. Merge two different Signal group tickets → verify validation/warning - ---- - -## References - -- Zammad merge implementation: `app/models/ticket.rb:330-450` -- Zammad split implementation: `app/models/form_updater/concerns/applies_split_ticket_article.rb` -- Zammad email follow-up filter: `app/models/channel/filter/follow_up_merged.rb` -- Bridge WhatsApp controller: `packages/zammad-addon-bridge/src/app/controllers/channels_cdr_whatsapp_controller.rb` -- Bridge Signal controller: `packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb` -- Bridge WhatsApp job: `packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_whatsapp_job.rb` -- Bridge Signal job: `packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb` diff --git a/docs/ticket-field-propagation-design.md b/docs/ticket-field-propagation-design.md deleted file mode 100644 index 61d4bec..0000000 --- a/docs/ticket-field-propagation-design.md +++ /dev/null @@ -1,906 +0,0 @@ -# Ticket Field Propagation System - -## Overview - -A configurable system for copying/syncing fields between related tickets (parent/child, merged, linked). This addresses the bridge channel preferences problem while providing a general-purpose solution for custom fields. - -## Problem Statement - -Zammad creates relationships between tickets through: -- **Split**: Creates child ticket from parent's article -- **Merge**: Source ticket becomes child of target (merged state) -- **Manual linking**: Agents can link tickets as parent/child or related - -Currently, no field values are propagated across these relationships except basic attributes on split. This causes issues when: -- Bridge channel metadata needs to follow the conversation -- Custom fields (account ID, region, priority score) should be inherited -- Parent ticket context should flow to children (or vice versa) - -## Use Cases - -### Use Case 1: Bridge Channel Inheritance (Immediate Need) -When a ticket is split or merged, the bridge channel metadata (`preferences.cdr_whatsapp`, `preferences.cdr_signal`) should be copied so agents can reply via the same channel. - -### Use Case 2: Custom Field Inheritance -Organization uses custom fields like `account_tier`, `region`, `contract_id`. When splitting a ticket, the child should inherit these values. - -### Use Case 3: Escalation Propagation -When a child ticket is escalated (custom `escalation_level` field), the parent should be updated to reflect this. - -### Use Case 4: SLA Context -Parent ticket has SLA deadline. Child tickets should inherit or reference this deadline. - -### Use Case 5: Bulk Operations -When updating a parent ticket's category, optionally cascade to all children. - ---- - -## Design - -### Terminology - -| Term | Definition | -|------|------------| -| **Source** | The ticket providing the field value | -| **Target** | The ticket receiving the field value | -| **Direction** | Which way data flows (parent→child, child→parent, source→target on merge) | -| **Trigger** | The event that initiates propagation (split, merge, update, link_create) | -| **Condition** | When to apply the copy (always, if_empty, if_greater, custom) | -| **Field Path** | Dot-notation path to the field (`preferences.cdr_whatsapp.chat_id`) | - -### Field Types - -The system must handle different field storage mechanisms: - -```ruby -# 1. Standard ticket attributes -ticket.group_id -ticket.priority_id -ticket.organization_id - -# 2. Preferences hash (nested) -ticket.preferences['channel_id'] -ticket.preferences['cdr_whatsapp']['chat_id'] -ticket.preferences['cdr_signal']['group_joined'] - -# 3. Custom object attributes (Zammad ObjectManager) -ticket.custom_account_id # Added via Admin → Objects → Ticket -ticket.custom_region -ticket.custom_escalation_level - -# 4. Tags (special handling) -ticket.tag_list # Array of strings -``` - -### Configuration Schema - -```ruby -# Stored in Setting or dedicated table -TicketFieldPropagation.configure do |config| - - # Define field groups for convenience - config.field_group :bridge_channel, [ - 'preferences.channel_id', - 'preferences.cdr_whatsapp', # Copies entire hash - 'preferences.cdr_signal', - 'preferences.cdr_voice' - ] - - config.field_group :customer_context, [ - 'organization_id', - 'custom_account_id', - 'custom_region', - 'custom_contract_id' - ] - - config.field_group :sla_context, [ - 'custom_sla_deadline', - 'custom_escalation_level' - ] - - # Define propagation rules - - # Bridge preferences: copy to child on split if child doesn't have them - config.rule :bridge_on_split do |r| - r.fields :bridge_channel - r.trigger :split - r.direction :parent_to_child - r.condition :if_target_empty - r.timing :immediate - end - - # Bridge preferences: copy to target on merge if target doesn't have them - config.rule :bridge_on_merge do |r| - r.fields :bridge_channel - r.trigger :merge - r.direction :source_to_target - r.condition :if_target_empty - r.timing :immediate - end - - # Customer context: always copy to child on split - config.rule :customer_context_on_split do |r| - r.fields :customer_context - r.trigger :split - r.direction :parent_to_child - r.condition :always - r.timing :immediate - end - - # Escalation: propagate highest level to parent - config.rule :escalation_to_parent do |r| - r.fields ['custom_escalation_level'] - r.trigger :update - r.direction :child_to_parent - r.condition :if_greater - r.timing :deferred # Use job queue - end - - # Manual sync: allow agent to trigger full sync - config.rule :manual_sync do |r| - r.fields [:customer_context, :sla_context] - r.trigger :manual - r.direction :parent_to_children # All children - r.condition :always - r.timing :immediate - end -end -``` - -### Alternative: JSON Configuration - -For storage in Zammad's `Setting` table: - -```json -{ - "field_groups": { - "bridge_channel": [ - "preferences.channel_id", - "preferences.cdr_whatsapp", - "preferences.cdr_signal" - ], - "customer_context": [ - "organization_id", - "custom_account_id", - "custom_region" - ] - }, - "rules": [ - { - "name": "bridge_on_split", - "fields": ["@bridge_channel"], - "trigger": "split", - "direction": "parent_to_child", - "condition": "if_target_empty", - "enabled": true - }, - { - "name": "bridge_on_merge", - "fields": ["@bridge_channel"], - "trigger": "merge", - "direction": "source_to_target", - "condition": "if_target_empty", - "enabled": true - }, - { - "name": "customer_context_inherit", - "fields": ["@customer_context"], - "trigger": "split", - "direction": "parent_to_child", - "condition": "always", - "enabled": true - } - ] -} -``` - ---- - -## Architecture - -### Components - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ TicketFieldPropagation │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ -│ │ Configuration│ │ Engine │ │ FieldAccessor │ │ -│ │ │───▶│ │───▶│ │ │ -│ │ - field_groups │ - execute() │ │ - get(path) │ │ -│ │ - rules │ │ - apply_rule │ │ - set(path, val) │ │ -│ │ - load/save │ │ - find_related │ - deep_merge │ │ -│ └──────────────┘ └──────────────┘ └──────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌──────────────────────────────────────────────────────────┐ │ -│ │ RelationshipFinder │ │ -│ │ │ │ -│ │ - find_parent(ticket) │ │ -│ │ - find_children(ticket) │ │ -│ │ - find_merge_target(ticket) │ │ -│ │ - find_merge_source(ticket) │ │ -│ └──────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ Triggers │ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌────────────────┐ ┌────────────────┐ ┌─────────────────┐ │ -│ │ Ticket Concern │ │ Transaction │ │ Manual API │ │ -│ │ │ │ Observer │ │ Endpoint │ │ -│ │ after_create │ │ │ │ │ │ -│ │ after_update │ │ on merge event │ │ POST /tickets/ │ │ -│ │ after_save │ │ on split event │ │ :id/propagate │ │ -│ └────────────────┘ └────────────────┘ └─────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ -``` - -### Class Design - -```ruby -# lib/ticket_field_propagation/configuration.rb -module TicketFieldPropagation - class Configuration - attr_accessor :field_groups, :rules - - def self.load - # Load from Setting table or YAML - end - - def field_group(name, fields) - @field_groups[name] = fields - end - - def rule(name, &block) - rule = Rule.new(name) - block.call(rule) - @rules << rule - end - - def expand_fields(field_refs) - # Expand @group_name references to actual field list - field_refs.flat_map do |ref| - if ref.start_with?('@') - @field_groups[ref[1..].to_sym] || [] - else - [ref] - end - end - end - end - - class Rule - attr_accessor :name, :fields, :trigger, :direction, :condition, :timing - - def initialize(name) - @name = name - @timing = :immediate - @condition = :always - end - - def applies_to?(event_type) - @trigger == event_type || (@trigger.is_a?(Array) && @trigger.include?(event_type)) - end - end -end -``` - -```ruby -# lib/ticket_field_propagation/engine.rb -module TicketFieldPropagation - class Engine - def initialize(source_ticket, event_type, target_ticket: nil) - @source = source_ticket - @event = event_type - @explicit_target = target_ticket - @config = Configuration.load - end - - def execute - applicable_rules.each do |rule| - if rule.timing == :deferred - PropagationJob.perform_later(@source.id, rule.name) - else - apply_rule(rule) - end - end - end - - private - - def applicable_rules - @config.rules.select { |r| r.applies_to?(@event) && r.enabled } - end - - def apply_rule(rule) - targets = find_targets(rule.direction) - fields = @config.expand_fields(rule.fields) - - targets.each do |target| - source = determine_source(rule.direction, target) - PropagationResult.log(@source, target, rule) - - fields.each do |field_path| - copy_field(source, target, field_path, rule.condition) - end - - target.save! if target.changed? - end - end - - def find_targets(direction) - case direction - when :parent_to_child, :parent_to_children - RelationshipFinder.find_children(@source) - when :child_to_parent - [RelationshipFinder.find_parent(@source)].compact - when :source_to_target - [@explicit_target || RelationshipFinder.find_merge_target(@source)].compact - else - [] - end - end - - def determine_source(direction, target) - case direction - when :parent_to_child, :parent_to_children, :source_to_target - @source - when :child_to_parent - target # We're copying FROM child TO parent, so target is source here - # Wait, this is confusing. Let me reconsider... - @source # The ticket that triggered the event is the source - end - end - - def copy_field(source, target, field_path, condition) - source_value = FieldAccessor.get(source, field_path) - return if source_value.nil? - - target_value = FieldAccessor.get(target, field_path) - - case condition - when :if_target_empty - return if target_value.present? - when :if_greater - return if target_value.present? && target_value >= source_value - when :always - # proceed - end - - FieldAccessor.set(target, field_path, source_value) - end - end -end -``` - -```ruby -# lib/ticket_field_propagation/field_accessor.rb -module TicketFieldPropagation - class FieldAccessor - class << self - def get(ticket, field_path) - parts = field_path.split('.') - - value = ticket - parts.each do |part| - value = access_part(value, part) - return nil if value.nil? - end - - # Deep dup hashes to prevent mutation - value.is_a?(Hash) ? value.deep_dup : value - end - - def set(ticket, field_path, value) - parts = field_path.split('.') - - if parts.length == 1 - # Direct attribute - set_attribute(ticket, parts[0], value) - else - # Nested in preferences or similar - set_nested(ticket, parts, value) - end - end - - private - - def access_part(object, part) - if object.is_a?(Hash) - object[part] || object[part.to_sym] - elsif object.respond_to?(part) - object.send(part) - elsif object.respond_to?(:[]) - object[part] - else - nil - end - end - - def set_attribute(ticket, attr_name, value) - if ticket.respond_to?("#{attr_name}=") - ticket.send("#{attr_name}=", value) - else - raise ArgumentError, "Unknown attribute: #{attr_name}" - end - end - - def set_nested(ticket, parts, value) - # e.g., ['preferences', 'cdr_whatsapp'] - root = parts[0] - - if root == 'preferences' - ticket.preferences ||= {} - set_hash_path(ticket.preferences, parts[1..], value) - else - raise ArgumentError, "Unsupported nested path root: #{root}" - end - end - - def set_hash_path(hash, remaining_parts, value) - if remaining_parts.length == 1 - key = remaining_parts[0] - if value.is_a?(Hash) && hash[key].is_a?(Hash) - # Deep merge for hash values - hash[key] = hash[key].deep_merge(value) - else - hash[key] = value - end - else - key = remaining_parts[0] - hash[key] ||= {} - set_hash_path(hash[key], remaining_parts[1..], value) - end - end - end - end -end -``` - -```ruby -# lib/ticket_field_propagation/relationship_finder.rb -module TicketFieldPropagation - class RelationshipFinder - class << self - def find_parent(ticket) - # In Zammad links: parent ticket has link_type 'child' pointing to it - # Wait, need to verify Zammad's link semantics... - # - # From merge: source ticket gets a 'parent' link pointing TO target - # Link.add(link_type: 'parent', source: target_id, target: source_id) - # - # So to find parent of a ticket, look for 'parent' links where - # this ticket is the target (link_object_target_value) - - links = Link.list( - link_object: 'Ticket', - link_object_value: ticket.id - ) - - parent_link = links.find { |l| l['link_type'] == 'parent' } - return nil unless parent_link - - Ticket.find_by(id: parent_link['link_object_value']) - end - - def find_children(ticket) - links = Link.list( - link_object: 'Ticket', - link_object_value: ticket.id - ) - - child_links = links.select { |l| l['link_type'] == 'child' } - child_ids = child_links.map { |l| l['link_object_value'] } - - Ticket.where(id: child_ids).to_a - end - - def find_merge_target(ticket) - # Merged ticket has 'parent' link to target - return nil unless ticket.state.state_type.name == 'merged' - find_parent(ticket) - end - - def find_merge_sources(ticket) - # Find tickets that were merged into this one - links = Link.list( - link_object: 'Ticket', - link_object_value: ticket.id - ) - - # Look for child links where the child is in merged state - child_links = links.select { |l| l['link_type'] == 'child' } - - child_links.filter_map do |link| - child = Ticket.find_by(id: link['link_object_value']) - child if child&.state&.state_type&.name == 'merged' - end - end - end - end -end -``` - -### Integration Points - -#### 1. Ticket Concern (for create/update triggers) - -```ruby -# app/models/ticket/field_propagation.rb -module Ticket::FieldPropagation - extend ActiveSupport::Concern - - included do - after_create :trigger_propagation_on_create - after_update :trigger_propagation_on_update - end - - private - - def trigger_propagation_on_create - # Check if this is a split (has parent link created simultaneously) - # This is tricky because link might be created after ticket... - # May need to hook into Link.add instead - end - - def trigger_propagation_on_update - return unless saved_change_to_attribute?(:state_id) - - if state.state_type.name == 'merged' - TicketFieldPropagation::Engine.new(self, :merge).execute - end - end -end -``` - -#### 2. Transaction Observer (for merge/split events) - -```ruby -# app/models/transaction/ticket_field_propagation.rb -class Transaction::TicketFieldPropagation - def self.execute(object, type, _changes, user_id, _options) - return unless object.is_a?(Ticket) - - case type - when 'update.merged_into' - # Source ticket was merged - propagate to target - target = TicketFieldPropagation::RelationshipFinder.find_merge_target(object) - TicketFieldPropagation::Engine.new(object, :merge, target_ticket: target).execute - - when 'update.received_merge' - # Target ticket received a merge - could trigger reverse propagation if needed - - when 'create' - # Check if this is from a split (check for immediate parent link) - parent = TicketFieldPropagation::RelationshipFinder.find_parent(object) - if parent.present? - TicketFieldPropagation::Engine.new(parent, :split, target_ticket: object).execute - end - end - end -end -``` - -#### 3. Manual API Endpoint - -```ruby -# app/controllers/ticket_field_propagation_controller.rb -class TicketFieldPropagationController < ApplicationController - before_action :authenticate_and_authorize - - # POST /api/v1/tickets/:id/propagate - def propagate - ticket = Ticket.find(params[:id]) - direction = params[:direction] || 'to_children' - fields = params[:fields] || 'all' - - case direction - when 'to_children' - engine = TicketFieldPropagation::Engine.new(ticket, :manual) - engine.execute_for_fields(fields, direction: :parent_to_children) - when 'from_parent' - parent = TicketFieldPropagation::RelationshipFinder.find_parent(ticket) - return render json: { error: 'No parent ticket' }, status: :not_found unless parent - - engine = TicketFieldPropagation::Engine.new(parent, :manual, target_ticket: ticket) - engine.execute_for_fields(fields, direction: :parent_to_child) - end - - render json: { success: true } - end - - # GET /api/v1/tickets/:id/propagation_preview - def preview - # Show what would be copied without doing it - end -end -``` - ---- - -## Handling Edge Cases - -### 1. Circular Reference Prevention - -```ruby -class Engine - MAX_DEPTH = 5 - - def execute(depth: 0) - return if depth >= MAX_DEPTH - - # Track processed tickets in this chain - Thread.current[:propagation_chain] ||= Set.new - return if Thread.current[:propagation_chain].include?(@source.id) - - Thread.current[:propagation_chain].add(@source.id) - - begin - # ... execute rules - ensure - Thread.current[:propagation_chain].delete(@source.id) - end - end -end -``` - -### 2. Conflicting Values on Merge - -When both source and target have values, the default is "don't overwrite" (`if_target_empty`). But we could support strategies: - -```ruby -config.rule :merge_preferences do |r| - r.fields ['preferences.cdr_whatsapp'] - r.trigger :merge - r.direction :source_to_target - r.condition :merge_hash # Deep merge instead of replace -end -``` - -Or with explicit conflict resolution: - -```ruby -r.on_conflict do |source_val, target_val, field| - case field - when /escalation/ - [source_val, target_val].max - when /preferences\.cdr_/ - target_val.presence || source_val # Keep target if present - else - source_val # Default: source wins - end -end -``` - -### 3. Multiple Bridge Channels - -If source has WhatsApp and target has Signal, we might want both: - -```ruby -# Current behavior with if_target_empty: -# - Source: {cdr_whatsapp: {...}} -# - Target: {cdr_signal: {...}} -# - Result: Target keeps cdr_signal, gains cdr_whatsapp (both present) - -# This works because we check per-field, not per-category -``` - -### 4. Signal Group Merge Validation - -Special case: don't allow merging tickets from different Signal groups: - -```ruby -config.rule :validate_signal_merge do |r| - r.trigger :merge - r.validator ->(source, target) { - source_group = source.preferences.dig('cdr_signal', 'chat_id') - target_group = target.preferences.dig('cdr_signal', 'chat_id') - - # OK if either doesn't have signal, or same group - return true if source_group.blank? || target_group.blank? - return true if source_group == target_group - - # Different groups - block the merge - raise Exceptions::UnprocessableEntity, - "Cannot merge tickets from different Signal groups" - } -end -``` - ---- - -## Audit Trail - -Track what was propagated for debugging and transparency: - -```ruby -# app/models/ticket_field_propagation_log.rb -class TicketFieldPropagationLog < ApplicationRecord - belongs_to :source_ticket, class_name: 'Ticket' - belongs_to :target_ticket, class_name: 'Ticket' - - # Columns: - # - source_ticket_id - # - target_ticket_id - # - rule_name - # - field_path - # - old_value (serialized) - # - new_value (serialized) - # - trigger_event - # - created_by_id - # - created_at -end -``` - -Or simpler: add to ticket history: - -```ruby -target_ticket.history_log( - 'field_propagated', - UserInfo.current_user_id, - value_from: source_ticket.id, - value_to: { field: field_path, value: new_value.to_s.truncate(100) } -) -``` - ---- - -## Configuration UI (Future) - -Admin interface at Settings → Ticket → Field Propagation: - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ Field Propagation Rules [+Add]│ -├─────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ [✓] Bridge Channel on Split [Edit] │ │ -│ │ Copy: preferences.cdr_whatsapp, preferences.cdr_signal │ │ -│ │ When: Ticket is split │ │ -│ │ Direction: Parent → Child │ │ -│ │ Condition: Only if child field is empty │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ [✓] Bridge Channel on Merge [Edit] │ │ -│ │ Copy: preferences.cdr_whatsapp, preferences.cdr_signal │ │ -│ │ When: Ticket is merged │ │ -│ │ Direction: Source → Target │ │ -│ │ Condition: Only if target field is empty │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ [ ] Customer Context Inheritance [Edit] │ │ -│ │ Copy: organization_id, custom_account_id, custom_region │ │ -│ │ When: Ticket is split │ │ -│ │ Direction: Parent → Child │ │ -│ │ Condition: Always │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────┘ -``` - ---- - -## Default Configuration - -Out-of-the-box settings that solve the bridge preferences problem: - -```json -{ - "field_groups": { - "bridge_channel": [ - "preferences.channel_id", - "preferences.cdr_whatsapp", - "preferences.cdr_signal", - "preferences.cdr_voice" - ] - }, - "rules": [ - { - "name": "bridge_on_split", - "description": "Copy bridge channel info when splitting tickets", - "fields": ["@bridge_channel"], - "trigger": "split", - "direction": "parent_to_child", - "condition": "if_target_empty", - "enabled": true - }, - { - "name": "bridge_on_merge", - "description": "Copy bridge channel info when merging tickets", - "fields": ["@bridge_channel"], - "trigger": "merge", - "direction": "source_to_target", - "condition": "if_target_empty", - "enabled": true - } - ] -} -``` - ---- - -## Implementation Phases - -### Phase 1: Core Engine (Solves Bridge Problem) -- FieldAccessor with dot-notation support -- RelationshipFinder for parent/child/merge relationships -- Engine with basic rule processing -- Hardcoded rules for bridge channel propagation -- Integration with Ticket merge (via concern or observer) - -### Phase 2: Configuration System -- JSON configuration in Setting table -- Field groups support -- Multiple condition types (if_empty, always, if_greater) -- Deferred execution via jobs - -### Phase 3: Split Integration -- Hook into ticket split workflow -- Detect parent relationship after split -- Apply split rules - -### Phase 4: Manual Triggers -- API endpoint for manual propagation -- Preview endpoint -- Audit logging - -### Phase 5: Admin UI -- Configuration interface in Zammad admin -- Visual rule builder -- Field picker for custom object attributes - -### Phase 6: Advanced Features -- Bidirectional sync -- Conflict resolution strategies -- Cascading updates -- Validation rules (like Signal group merge prevention) - ---- - -## Files to Create - -``` -packages/zammad-addon-bridge/src/ -├── lib/ -│ └── ticket_field_propagation/ -│ ├── configuration.rb -│ ├── engine.rb -│ ├── field_accessor.rb -│ ├── relationship_finder.rb -│ └── propagation_job.rb -├── app/ -│ ├── models/ -│ │ └── ticket/ -│ │ └── field_propagation.rb # Concern -│ └── controllers/ -│ └── ticket_field_propagation_controller.rb -├── config/ -│ └── initializers/ -│ └── ticket_field_propagation.rb # Default config & include concern -└── db/ - └── seeds/ - └── field_propagation_settings.rb -``` - ---- - -## Relationship to Bridge Preferences Problem - -The bridge preferences problem from the previous investigation is solved by: - -1. **Default rule `bridge_on_merge`**: Copies `preferences.cdr_whatsapp` and `preferences.cdr_signal` from source to target when tickets are merged, if target doesn't already have them. - -2. **Default rule `bridge_on_split`**: Copies the same preferences from parent to child when tickets are split. - -3. **Extensibility**: Additional custom fields can be added to propagation rules without code changes. - -This makes the field propagation system a superset solution that handles the immediate bridge problem while providing a framework for future field synchronization needs. diff --git a/package.json b/package.json index e5d4c1a..0a59707 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "Link from the Center for Digital Resilience", "scripts": { "dev": "dotenv -- turbo dev", diff --git a/packages/bridge-common/package.json b/packages/bridge-common/package.json index eecd5ee..91b9696 100644 --- a/packages/bridge-common/package.json +++ b/packages/bridge-common/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/bridge-common", - "version": "3.5.0-beta.1", + "version": "3.3.5", "main": "build/main/index.js", "type": "module", "author": "Darren Clarke ", diff --git a/packages/bridge-ui/package.json b/packages/bridge-ui/package.json index a2ca393..2e4d6fa 100644 --- a/packages/bridge-ui/package.json +++ b/packages/bridge-ui/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/bridge-ui", - "version": "3.5.0-beta.1", + "version": "3.3.5", "scripts": { "build": "tsc -p tsconfig.json" }, diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 331d500..c579d56 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/eslint-config", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "amigo's eslint config", "main": "index.js", "author": "Abel Luck ", diff --git a/packages/jest-config/package.json b/packages/jest-config/package.json index 1d845b3..591a33a 100644 --- a/packages/jest-config/package.json +++ b/packages/jest-config/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/jest-config", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "", "main": "index.js", "author": "Abel Luck ", diff --git a/packages/logger/package.json b/packages/logger/package.json index 9948916..cda0e71 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/logger", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "Shared logging utility for Link Stack monorepo", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/packages/signal-api/package.json b/packages/signal-api/package.json index bf17ff6..0905a16 100644 --- a/packages/signal-api/package.json +++ b/packages/signal-api/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/signal-api", - "version": "3.5.0-beta.1", + "version": "3.3.5", "type": "module", "main": "build/index.js", "exports": { diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index a5f03be..98021b8 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/typescript-config", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "Shared TypeScript config", "license": "AGPL-3.0-or-later", "author": "Abel Luck ", diff --git a/packages/ui/package.json b/packages/ui/package.json index bb5f0a1..a12655e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/ui", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "", "scripts": { "build": "tsc -p tsconfig.json" diff --git a/packages/zammad-addon-bridge/package.json b/packages/zammad-addon-bridge/package.json index f0f1e93..a7ef115 100644 --- a/packages/zammad-addon-bridge/package.json +++ b/packages/zammad-addon-bridge/package.json @@ -1,7 +1,7 @@ { "name": "@link-stack/zammad-addon-bridge", "displayName": "Bridge", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "An addon that adds CDR Bridge channels to Zammad.", "scripts": { "build": "node '../zammad-addon-common/dist/build.js'", diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee index 3ed6865..f8f8028 100644 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee +++ b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_channel/cdr_signal.coffee @@ -7,8 +7,6 @@ class ChannelCdrSignal extends App.ControllerSubContent 'click .js-disable': 'disable' 'click .js-enable': 'enable' 'click .js-rotate-token': 'rotateToken' - 'click .js-set-notification': 'setNotification' - 'click .js-unset-notification': 'unsetNotification' constructor: -> super @@ -43,8 +41,6 @@ class ChannelCdrSignal extends App.ControllerSubContent channels.push channel @html App.view('cdr_signal/index')( channels: channels - notificationEnabled: data.notification_enabled - notificationChannelId: data.notification_channel_id ) new: (e) => @@ -128,31 +124,6 @@ class ChannelCdrSignal extends App.ControllerSubContent @load() ) - setNotification: (e) => - e.preventDefault() - id = $(e.target).closest('.action').data('id') - @ajax( - id: 'cdr_signal_set_notification' - type: 'POST' - url: "#{@apiPath}/channels_cdr_signal_set_notification" - data: JSON.stringify(id: id) - processData: true - success: => - @load() - ) - - unsetNotification: (e) => - e.preventDefault() - @ajax( - id: 'cdr_signal_unset_notification' - type: 'POST' - url: "#{@apiPath}/channels_cdr_signal_unset_notification" - data: JSON.stringify({}) - processData: true - success: => - @load() - ) - class FormAdd extends App.ControllerModal head: 'Add Web Form' shown: true diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_profile/notification.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_profile/notification.coffee deleted file mode 100644 index 8a2c7f6..0000000 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_profile/notification.coffee +++ /dev/null @@ -1,220 +0,0 @@ -class ProfileNotification extends App.ControllerSubContent - @include App.TicketNotificationMatrix - - @requiredPermission: 'user_preferences.notifications+ticket.agent' - header: __('Notifications') - events: - 'submit form': 'update' - 'click .js-reset' : 'reset' - 'change .js-notificationSound': 'previewSound' - 'change #profile-groups-limit': 'didSwitchGroupsLimit' - 'change input[name=group_ids]': 'didChangeGroupIds' - 'change input[name$=".channel.signal"]': 'didChangeSignalCheckbox' - - elements: - '#profile-groups-limit': 'profileGroupsLimitInput' - '.profile-groups-limit-settings-inner': 'groupsLimitSettings' - '.profile-groups-all-unchecked': 'groupsAllUncheckedWarning' - - sounds: [ - { - name: 'Bell' - file: 'Bell.mp3' - }, - { - name: 'Kalimba' - file: 'Kalimba.mp3' - }, - { - name: 'Marimba' - file: 'Marimba.mp3' - }, - { - name: 'Peep' - file: 'Peep.mp3' - }, - { - name: 'Plop' - file: 'Plop.mp3' - }, - { - name: 'Ring' - file: 'Ring.mp3' - }, - { - name: 'Space' - file: 'Space.mp3' - }, - { - name: 'Wood' - file: 'Wood.mp3' - }, - { - name: 'Xylo' - file: 'Xylo.mp3' - } - ] - - constructor: -> - super - App.User.full(App.Session.get().id, @render, true, true) - - render: => - - matrix = - create: - name: __('New Ticket') - update: - name: __('Ticket update') - reminder_reached: - name: __('Ticket reminder reached') - escalation: - name: __('Ticket escalation') - - config = - group_ids: [] - matrix: {} - - user_config = @Session.get('preferences').notification_config - if user_config - config = $.extend(true, {}, config, user_config) - - # groups - user_group_config = true - if !user_config || !user_config['group_ids'] || _.isEmpty(user_config['group_ids']) || user_config['group_ids'][0] is '-' - user_group_config = false - - groups = [] - group_ids = App.User.find(@Session.get('id')).allGroupIds() - if group_ids - for group_id in group_ids - group = App.Group.find(group_id) - groups.push group - if !user_group_config - if !config['group_ids'] - config['group_ids'] = [] - config['group_ids'].push group_id.toString() - - groups = _.sortBy(groups, (item) -> return item.name) - - for sound in @sounds - sound.selected = sound.file is App.OnlineNotification.soundFile() ? true : false - - signal_notification_enabled = App.Config.get('signal_notification_enabled') - - signal_uid = config.signal_uid || '' - - # Check if any signal checkbox is currently checked in the matrix - signal_has_checked = false - if signal_notification_enabled - for key, val of config.matrix - if val?.channel?.signal - signal_has_checked = true - break - - @html App.view('profile/notification') - matrixTableHTML: @renderNotificationMatrix(config.matrix) - groups: groups - config: config - sounds: @sounds - notificationSoundEnabled: App.OnlineNotification.soundEnabled() - user_group_config: user_group_config - signal_notification_enabled: signal_notification_enabled - signal_uid: signal_uid - signal_has_checked: signal_has_checked - - update: (e) => - - #notification_config - e.preventDefault() - params = {} - params.notification_config = {} - - formParams = @formParam(e.target) - - params.notification_config.matrix = @updatedNotificationMatrixValues(formParams) - - if formParams.signal_uid? - params.notification_config.signal_uid = formParams.signal_uid - - if @profileGroupsLimitInput.is(':checked') - params.notification_config.group_ids = formParams['group_ids'] - if typeof params.notification_config.group_ids isnt 'object' - params.notification_config.group_ids = [params.notification_config.group_ids] - - if _.isEmpty(params.notification_config.group_ids) - delete params.notification_config.group_ids - - @formDisable(e) - - params.notification_sound = formParams.notification_sound - if !params.notification_sound.enabled - params.notification_sound.enabled = false - else - params.notification_sound.enabled = true - - # get data - @ajax( - id: 'preferences' - type: 'PUT' - url: @apiPath + '/users/preferences' - data: JSON.stringify(params) - processData: true - success: @success - error: @error - ) - - reset: (e) => - new App.ControllerConfirm( - message: __('Are you sure? Your notifications settings will be reset to default.') - buttonClass: 'btn--danger' - callback: => - @ajax( - id: 'preferences_notifications_reset' - type: 'POST' - url: "#{@apiPath}/users/preferences_notifications_reset" - processData: true - success: @success - ) - container: @el.closest('.content') - ) - - - success: (data, status, xhr) => - App.User.full( - App.Session.get('id'), - => - App.Event.trigger('ui:rerender') - @notify( - type: 'success' - msg: __('Update successful.') - ) - , - true - ) - - error: (xhr, status, error) => - @render() - data = JSON.parse(xhr.responseText) - @notify( - type: 'error' - msg: data.message - ) - - previewSound: (e) => - params = @formParam(e.target) - return if !params.notification_sound - return if !params.notification_sound.file - App.OnlineNotification.play(params.notification_sound.file) - - didSwitchGroupsLimit: (e) => - @groupsLimitSettings.collapse('toggle') - - didChangeGroupIds: (e) => - @groupsAllUncheckedWarning.toggleClass 'hide', @el.find('input[name=group_ids]:checked').length != 0 - - didChangeSignalCheckbox: (e) => - hasChecked = @el.find('input[name$=".channel.signal"]:checked').length > 0 - @el.find('.js-signal-phone-container').toggle(hasChecked) - -App.Config.set('Notifications', { prio: 2600, name: __('Notifications'), parent: '#profile', target: '#profile/notifications', permission: ['user_preferences.notifications+ticket.agent'], controller: ProfileNotification }, 'NavBarProfile') diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_ui_element/notification_matrix.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_ui_element/notification_matrix.coffee deleted file mode 100644 index e7550c1..0000000 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/controllers/_ui_element/notification_matrix.coffee +++ /dev/null @@ -1,15 +0,0 @@ -# coffeelint: disable=camel_case_classes -class App.UiElement.notification_matrix - @render: (values, options = {}) -> - - matrixYAxe = - create: - name: __('New Ticket') - update: - name: __('Ticket update') - reminder_reached: - name: __('Ticket reminder reached') - escalation: - name: __('Ticket escalation') - - $( App.view('generic/notification_matrix')( matrixYAxe: matrixYAxe, values: values, signal_notification_enabled: options.signal_notification_enabled ) ) diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/lib/mixins/ticket_notification_matrix.coffee b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/lib/mixins/ticket_notification_matrix.coffee deleted file mode 100644 index 407d897..0000000 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/lib/mixins/ticket_notification_matrix.coffee +++ /dev/null @@ -1,29 +0,0 @@ -# Common handling for the notification matrix -App.TicketNotificationMatrix = - renderNotificationMatrix: (values) -> - App.UiElement.notification_matrix.render(values, signal_notification_enabled: App.Config.get('signal_notification_enabled'))[0].outerHTML - - updatedNotificationMatrixValues: (formParams) -> - matrix = {} - - for key, value of formParams - area = key.split('.') - - continue if area[0] isnt 'matrix' - - if !matrix[area[1]] - matrix[area[1]] = {} - - switch area[2] - when 'criteria' - if !matrix[area[1]][area[2]] - matrix[area[1]][area[2]] = {} - - matrix[area[1]][area[2]][area[3]] = value is 'true' - when 'channel' - if !matrix[area[1]][area[2]] - matrix[area[1]][area[2]] = { online: true } - - matrix[area[1]][area[2]][area[3]] = value is 'true' - - matrix diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/cdr_signal/index.jst.eco b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/cdr_signal/index.jst.eco index f8ae206..9bd157e 100644 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/cdr_signal/index.jst.eco +++ b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/cdr_signal/index.jst.eco @@ -20,9 +20,6 @@

<%- @Icon('status', 'supergood-color inline') %> <%= channel.options.phone_number %>

- <% if @notificationEnabled and @notificationChannelId is channel.id: %> - <%- @T('Agent Notifications') %> - <% end %>
@@ -45,11 +42,6 @@ <% else: %>
<%- @T('Enable') %>
<% end %> - <% if @notificationEnabled and @notificationChannelId is channel.id: %> -
<%- @T('Disable Agent Notifications') %>
- <% else if channel.active is true: %> -
<%- @T('Use for Agent Notifications') %>
- <% end %>
<%- @T('Edit') %>
diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/generic/notification_matrix.jst.eco b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/generic/notification_matrix.jst.eco deleted file mode 100644 index 49c9e1a..0000000 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/generic/notification_matrix.jst.eco +++ /dev/null @@ -1,70 +0,0 @@ -<% if @signal_notification_enabled: %> - <% colWidth = "13%" %> - <% channelWidth = "100px" %> -<% else: %> - <% colWidth = "16%" %> - <% channelWidth = "120px" %> -<% end %> - - - - - - <% if @matrixYAxe: %> - <% for key, value of @matrixYAxe: %> - - -
- <%- @T('My Tickets') %> - <%- @T('Not Assigned') %>* - <%- @T('Subscribed Tickets') %> - <%- @T('All Tickets') %>* - <%- @T('Also notify via email') %> - <% if @signal_notification_enabled: %> - <%- @T('Also notify via Signal') %> - <% end %> -
- <%- @T(value.name) %> - <% criteria = @values[key]?.criteria %> - <% channel = @values[key]?.channel %> - - - - - - - - - - - <% if @signal_notification_enabled: %> - - - <% end %> - <% end %> - <% end %> -
diff --git a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/profile/notification.jst.eco b/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/profile/notification.jst.eco deleted file mode 100644 index 65e43f9..0000000 --- a/packages/zammad-addon-bridge/src/app/assets/javascripts/app/views/profile/notification.jst.eco +++ /dev/null @@ -1,86 +0,0 @@ - - -
- -
- <%- @matrixTableHTML %> -
- - <% if @signal_notification_enabled: %> -
-

<%- @T('Signal Phone Number') %>

-
- -

<%- @T('Use international format with country code (e.g., +1234567890)') %>

-
-
- <% end %> - - <% if @groups: %> -
- checked <% end %>> - -
-

- <%- @T('Limit Groups') %> -

- -
-
- - - - - - - - <% for group in @groups: %> - - -
<%- @T('Group') %> - <%- @T('Not Assigned') %> & <%- @T('All Tickets') %> -
<%- @P(group, 'name') %> - - - <% end %> -
-
-
- <% end %> - -

<%- @T('Sounds') %>

-
-
- -
-
- - <%- @Icon('arrow-down') %> -
-
-
- -
- - - -
diff --git a/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb b/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb index 8b5dae0..f09c458 100644 --- a/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb +++ b/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_signal_controller.rb @@ -15,34 +15,10 @@ class ChannelsCdrSignalController < ApplicationController end render json: { assets: assets, - channel_ids: channel_ids, - notification_enabled: Setting.get('signal_notification_enabled') == true, - notification_channel_id: Setting.get('signal_notification_channel_id') + channel_ids: channel_ids } end - def set_notification_channel - channel_id = params[:id].to_i - channel = Channel.find_by(id: channel_id, area: 'Signal::Number') - - unless channel - render json: { error: 'Channel not found' }, status: :not_found - return - end - - Setting.set('signal_notification_channel_id', channel_id) - Setting.set('signal_notification_enabled', true) - - render json: { success: true, notification_channel_id: channel_id } - end - - def unset_notification_channel - Setting.set('signal_notification_enabled', false) - Setting.set('signal_notification_channel_id', nil) - - render json: { success: true } - end - def add begin errors = {} @@ -178,31 +154,16 @@ class ChannelsCdrSignalController < ApplicationController return if Ticket::Article.exists?(message_id: "cdr_signal.#{message_id}") receiver_phone_number = params[:to].strip - sender_phone_number = params[:from].present? ? params[:from].strip : nil - sender_user_id = params[:user_id].present? ? params[:user_id].strip : nil + sender_phone_number = params[:from].strip # Check if this is a group message using the is_group flag from bridge-worker # This flag is set when: # 1. The original message came from a Signal group # 2. Bridge-worker created a new group for the conversation - is_group_message = params[:is_group].to_s == 'true' - - # Lookup customer with fallback chain: - # 1. Phone number in phone/mobile fields (preferred) - # 2. Signal user ID in signal_uid field - # 3. User ID in phone/mobile fields (legacy - we used to store UUIDs there) - customer = nil - if sender_phone_number.present? - customer = User.find_by(phone: sender_phone_number) - customer ||= User.find_by(mobile: sender_phone_number) - end - if customer.nil? && sender_user_id.present? - customer = User.find_by(signal_uid: sender_user_id) - # Legacy fallback: user ID might be stored in phone field - customer ||= User.find_by(phone: sender_user_id) - customer ||= User.find_by(mobile: sender_user_id) - end + is_group_message = params[:is_group].to_s == 'true' || params[:is_group].to_s == 'true' + customer = User.find_by(phone: sender_phone_number) + customer ||= User.find_by(mobile: sender_phone_number) unless customer role_ids = Role.signup_role_ids customer = User.create( @@ -210,8 +171,7 @@ class ChannelsCdrSignalController < ApplicationController lastname: '', email: '', password: '', - phone: sender_phone_number.presence || sender_user_id, - signal_uid: sender_user_id, + phone: sender_phone_number, note: 'CDR Signal', active: true, role_ids: role_ids, @@ -220,15 +180,6 @@ class ChannelsCdrSignalController < ApplicationController ) end - # Update signal_uid if we have it and customer doesn't - if sender_user_id.present? && customer.signal_uid.blank? - customer.update(signal_uid: sender_user_id) - end - # Update phone if we have it and customer only has user_id in phone field - if sender_phone_number.present? && customer.phone == sender_user_id - customer.update(phone: sender_phone_number) - end - # set current user UserInfo.current_user_id = customer.id current_user_set(customer, 'token_auth') @@ -257,8 +208,7 @@ class ChannelsCdrSignalController < ApplicationController attachment_data_base64 = params[:attachment] attachment_filename = params[:filename] attachment_mimetype = params[:mime_type] - sender_display = sender_phone_number.presence || sender_user_id - title = "Message from #{sender_display} at #{sent_at}" + title = "Message from #{sender_phone_number} at #{sent_at}" body = message # find ticket or create one @@ -268,7 +218,7 @@ class ChannelsCdrSignalController < ApplicationController Rails.logger.info "=== SIGNAL GROUP TICKET LOOKUP ===" Rails.logger.info "Looking for ticket with group_id: #{receiver_phone_number}" Rails.logger.info "Customer ID: #{customer.id}" - Rails.logger.info "Customer Phone: #{sender_display}" + Rails.logger.info "Customer Phone: #{sender_phone_number}" Rails.logger.info "Channel ID: #{channel.id}" begin @@ -306,21 +256,14 @@ class ChannelsCdrSignalController < ApplicationController ticket.state = Ticket::State.find_by(default_follow_up: true) if ticket.state_id != new_state.id else # Set up chat_id based on whether this is a group message - # For direct messages, prefer UUID (more stable than phone numbers which can change) - chat_id = is_group_message ? receiver_phone_number : (sender_user_id.presence || sender_phone_number) + chat_id = is_group_message ? receiver_phone_number : sender_phone_number # Build preferences with group_id included if needed cdr_signal_prefs = { - bot_token: channel.options[:bot_token], - chat_id: chat_id, - user_id: sender_user_id + bot_token: channel.options[:bot_token], # change to bot id + chat_id: chat_id } - # Store original recipient phone for group tickets to enable ticket splitting - if is_group_message - cdr_signal_prefs[:original_recipient] = sender_phone_number - end - Rails.logger.info "=== CREATING NEW TICKET ===" Rails.logger.info "Preferences to be stored:" Rails.logger.info " - channel_id: #{channel.id}" @@ -340,7 +283,7 @@ class ChannelsCdrSignalController < ApplicationController ticket.save! article_params = { - from: sender_display, + from: sender_phone_number, to: receiver_phone_number, sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id, subject: title, @@ -353,8 +296,7 @@ class ChannelsCdrSignalController < ApplicationController cdr_signal: { timestamp: sent_at, message_id: message_id, - from: sender_phone_number, - user_id: sender_user_id + from: sender_phone_number } } } @@ -432,24 +374,6 @@ class ChannelsCdrSignalController < ApplicationController return end - # Idempotency check: if chat_id is already a group ID, don't overwrite it - # This prevents race conditions where multiple group_created webhooks arrive - # (e.g., due to retries after API timeouts during group creation) - existing_chat_id = ticket.preferences&.dig(:cdr_signal, :chat_id) || - ticket.preferences&.dig('cdr_signal', 'chat_id') - if existing_chat_id&.start_with?('group.') - Rails.logger.info "Signal group update: Ticket #{ticket.id} already has group #{existing_chat_id}, ignoring new group #{params[:group_id]}" - render json: { - success: true, - skipped: true, - reason: 'Ticket already has a group assigned', - existing_group_id: existing_chat_id, - ticket_id: ticket.id, - ticket_number: ticket.number - }, status: :ok - return - end - # Update ticket preferences with the group information ticket.preferences ||= {} ticket.preferences[:cdr_signal] ||= {} @@ -534,36 +458,6 @@ class ChannelsCdrSignalController < ApplicationController Rails.logger.info "Signal group member #{member_phone} joined group #{params[:group_id]} for ticket #{ticket.id}" - # Check if any articles had a group_not_joined notification and add resolution note - # Only add resolution note if we previously notified about the delivery issue - articles_with_pending_notification = Ticket::Article.where(ticket_id: ticket.id) - .where("preferences LIKE ?", "%group_not_joined_note_added: true%") - - if articles_with_pending_notification.exists? - # Check if we already added a resolution note for this ticket - resolution_note_exists = Ticket::Article.where(ticket_id: ticket.id) - .where("preferences LIKE ?", "%group_joined_resolution: true%") - .exists? - - unless resolution_note_exists - Ticket::Article.create( - ticket_id: ticket.id, - content_type: 'text/plain', - body: 'Recipient has now joined the Signal group. Pending messages will be delivered shortly.', - internal: true, - sender: Ticket::Article::Sender.find_by(name: 'System'), - type: Ticket::Article::Type.find_by(name: 'note'), - preferences: { - delivery_message: true, - group_joined_resolution: true, - }, - updated_by_id: 1, - created_by_id: 1, - ) - Rails.logger.info "Ticket ##{ticket.number}: Added resolution note about customer joining Signal group" - end - end - render json: { success: true, ticket_id: ticket.id, diff --git a/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_whatsapp_controller.rb b/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_whatsapp_controller.rb index b273533..c30d5ee 100644 --- a/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_whatsapp_controller.rb +++ b/packages/zammad-addon-bridge/src/app/controllers/channels_cdr_whatsapp_controller.rb @@ -123,16 +123,12 @@ class ChannelsCdrWhatsappController < ApplicationController errors = {} %i[to + from message_id sent_at].each do |field| errors[field] = 'required' if params[field].blank? end - # At least one of from (phone) or user_id must be present - if params[:from].blank? && params[:user_id].blank? - errors[:from] = 'required (or user_id)' - end - if errors.present? render json: { errors: errors @@ -145,25 +141,9 @@ class ChannelsCdrWhatsappController < ApplicationController return if Ticket::Article.exists?(message_id: "cdr_whatsapp.#{message_id}") receiver_phone_number = params[:to].strip - sender_phone_number = params[:from].present? ? params[:from].strip : nil - sender_user_id = params[:user_id].present? ? params[:user_id].strip : nil - - # Lookup customer with fallback chain: - # 1. Phone number in phone/mobile fields (preferred) - # 2. WhatsApp user ID in whatsapp_uid field - # 3. User ID in phone/mobile fields (legacy - we used to store LIDs there) - customer = nil - if sender_phone_number.present? - customer = User.find_by(phone: sender_phone_number) - customer ||= User.find_by(mobile: sender_phone_number) - end - if customer.nil? && sender_user_id.present? - customer = User.find_by(whatsapp_uid: sender_user_id) - # Legacy fallback: user ID might be stored in phone field - customer ||= User.find_by(phone: sender_user_id) - customer ||= User.find_by(mobile: sender_user_id) - end - + sender_phone_number = params[:from].strip + customer = User.find_by(phone: sender_phone_number) + customer ||= User.find_by(mobile: sender_phone_number) unless customer role_ids = Role.signup_role_ids customer = User.create( @@ -171,8 +151,7 @@ class ChannelsCdrWhatsappController < ApplicationController lastname: '', email: '', password: '', - phone: sender_phone_number.presence || sender_user_id, - whatsapp_uid: sender_user_id, + phone: sender_phone_number, note: 'CDR Whatsapp', active: true, role_ids: role_ids, @@ -181,15 +160,6 @@ class ChannelsCdrWhatsappController < ApplicationController ) end - # Update whatsapp_uid if we have it and customer doesn't - if sender_user_id.present? && customer.whatsapp_uid.blank? - customer.update(whatsapp_uid: sender_user_id) - end - # Update phone if we have it and customer only has user_id in phone field - if sender_phone_number.present? && customer.phone == sender_user_id - customer.update(phone: sender_phone_number) - end - # set current user UserInfo.current_user_id = customer.id current_user_set(customer, 'token_auth') @@ -218,8 +188,7 @@ class ChannelsCdrWhatsappController < ApplicationController attachment_data_base64 = params[:attachment] attachment_filename = params[:filename] attachment_mimetype = params[:mime_type] - sender_display = sender_phone_number.presence || sender_user_id - title = "Message from #{sender_display} at #{sent_at}" + title = "Message from #{sender_phone_number} at #{sent_at}" body = message # find ticket or create one @@ -238,9 +207,8 @@ class ChannelsCdrWhatsappController < ApplicationController preferences: { channel_id: channel.id, cdr_whatsapp: { - bot_token: channel.options[:bot_token], - chat_id: sender_phone_number.presence || sender_user_id, - user_id: sender_user_id + bot_token: channel.options[:bot_token], # change to bot id + chat_id: sender_phone_number } } ) @@ -249,7 +217,7 @@ class ChannelsCdrWhatsappController < ApplicationController ticket.save! article_params = { - from: sender_display, + from: sender_phone_number, to: receiver_phone_number, sender_id: Ticket::Article::Sender.find_by(name: 'Customer').id, subject: title, @@ -262,8 +230,7 @@ class ChannelsCdrWhatsappController < ApplicationController cdr_whatsapp: { timestamp: sent_at, message_id: message_id, - from: sender_phone_number, - user_id: sender_user_id + from: sender_phone_number } } } diff --git a/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb b/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb index 786be4b..37e026a 100644 --- a/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb +++ b/packages/zammad-addon-bridge/src/app/jobs/communicate_cdr_signal_job.rb @@ -40,37 +40,10 @@ class CommunicateCdrSignalJob < ApplicationJob if is_group_chat && group_joined == false Rails.logger.info "Ticket ##{ticket.number}: User hasn't joined Signal group yet, skipping message delivery" - # Track group_not_joined retry attempts separately - article.preferences['group_not_joined_retry'] ||= 0 - article.preferences['group_not_joined_retry'] += 1 - # Mark article as pending delivery article.preferences['delivery_status'] = 'pending' article.preferences['delivery_status_message'] = 'Waiting for user to join Signal group' article.preferences['delivery_status_date'] = Time.zone.now - - # After 3 failed attempts, add a note to inform the agent (only once) - if article.preferences['group_not_joined_retry'] == 3 && !article.preferences['group_not_joined_note_added'] - Ticket::Article.create( - ticket_id: ticket.id, - content_type: 'text/plain', - body: 'Unable to send Signal message: Recipient has not yet joined the Signal group. ' \ - 'The message will be delivered automatically once they accept the group invitation.', - internal: true, - sender: Ticket::Article::Sender.find_by(name: 'System'), - type: Ticket::Article::Type.find_by(name: 'note'), - preferences: { - delivery_article_id_related: article.id, - delivery_message: true, - group_not_joined_notification: true, - }, - updated_by_id: 1, - created_by_id: 1, - ) - article.preferences['group_not_joined_note_added'] = true - Rails.logger.info "Ticket ##{ticket.number}: Added notification note about pending group join" - end - article.save! # Retry later when user might have joined diff --git a/packages/zammad-addon-bridge/src/app/jobs/signal_notification_job.rb b/packages/zammad-addon-bridge/src/app/jobs/signal_notification_job.rb deleted file mode 100644 index c960196..0000000 --- a/packages/zammad-addon-bridge/src/app/jobs/signal_notification_job.rb +++ /dev/null @@ -1,65 +0,0 @@ -# frozen_string_literal: true - -class SignalNotificationJob < ApplicationJob - retry_on StandardError, attempts: 3, wait: lambda { |executions| - executions * 60.seconds - } - - def perform(ticket_id:, article_id:, user_id:, type:, changes:) - ticket = Ticket.find_by(id: ticket_id) - return if !ticket - - user = User.find_by(id: user_id) - return if !user - - signal_uid = user.preferences.dig('notification_config', 'signal_uid').presence - return if signal_uid.blank? - - article = article_id ? Ticket::Article.find_by(id: article_id) : nil - - channel = signal_channel - return if !channel - - message = SignalNotificationSender.build_message( - ticket: ticket, - article: article, - user: user, - type: type, - changes: changes - ) - - return if message.blank? - - SignalNotificationSender.send_message( - channel: channel, - recipient: signal_uid, - message: message - ) - - add_history(ticket, user, signal_uid, type) - - Rails.logger.info "Sent Signal notification to #{signal_uid} for ticket ##{ticket.number} (#{type})" - end - - private - - def signal_channel - channel_id = Setting.get('signal_notification_channel_id') - return unless channel_id - - Channel.find_by(id: channel_id, area: 'Signal::Number', active: true) - end - - def add_history(ticket, user, signal_uid, type) - identifier = signal_uid.presence || user.login - recipient_list = "#{identifier}(#{type}:signal)" - - History.add( - o_id: ticket.id, - history_type: 'notification', - history_object: 'Ticket', - value_to: recipient_list, - created_by_id: 1 - ) - end -end diff --git a/packages/zammad-addon-bridge/src/app/models/link/setup_split_signal_group.rb b/packages/zammad-addon-bridge/src/app/models/link/setup_split_signal_group.rb deleted file mode 100644 index b7db245..0000000 --- a/packages/zammad-addon-bridge/src/app/models/link/setup_split_signal_group.rb +++ /dev/null @@ -1,51 +0,0 @@ -# frozen_string_literal: true - -module Link::SetupSplitSignalGroup - extend ActiveSupport::Concern - - included do - after_create :setup_signal_group_for_split_ticket - end - - private - - def setup_signal_group_for_split_ticket - # Only if auto-groups enabled - return unless ENV['BRIDGE_SIGNAL_AUTO_GROUPS'].to_s.downcase == 'true' - - # Only child links (splits create child->parent links) - return unless link_type_id == Link::Type.find_by(name: 'child')&.id - - # Only Ticket-to-Ticket links - ticket_object_id = Link::Object.find_by(name: 'Ticket')&.id - return unless link_object_source_id == ticket_object_id - return unless link_object_target_id == ticket_object_id - - child_ticket = Ticket.find_by(id: link_object_source_value) - parent_ticket = Ticket.find_by(id: link_object_target_value) - return unless child_ticket && parent_ticket - - # Only if parent has Signal group (chat_id starts with "group.") - parent_signal_prefs = parent_ticket.preferences&.dig('cdr_signal') - return unless parent_signal_prefs.present? - return unless parent_signal_prefs['chat_id']&.start_with?('group.') - - original_recipient = parent_signal_prefs['original_recipient'] - return unless original_recipient.present? - - # Set up child for lazy group creation: - # chat_id = phone number triggers new group on first message - child_ticket.preferences ||= {} - child_ticket.preferences['channel_id'] = parent_ticket.preferences['channel_id'] - child_ticket.preferences['cdr_signal'] = { - 'bot_token' => parent_signal_prefs['bot_token'], - 'chat_id' => original_recipient, # Phone number, NOT group ID - 'original_recipient' => original_recipient - } - # Set article type so Zammad shows Signal reply option - child_ticket.create_article_type_id = Ticket::Article::Type.find_by(name: 'cdr_signal')&.id - child_ticket.save! - - Rails.logger.info "Signal split: Ticket ##{child_ticket.number} set up for new group (recipient: #{original_recipient})" - end -end diff --git a/packages/zammad-addon-bridge/src/app/models/transaction/signal_notification.rb b/packages/zammad-addon-bridge/src/app/models/transaction/signal_notification.rb deleted file mode 100644 index 5cb2af0..0000000 --- a/packages/zammad-addon-bridge/src/app/models/transaction/signal_notification.rb +++ /dev/null @@ -1,143 +0,0 @@ -# frozen_string_literal: true - -class Transaction::SignalNotification - include ChecksHumanChanges - - def initialize(item, params = {}) - @item = item - @params = params - end - - def perform - return if Setting.get('import_mode') - return if %w[Ticket Ticket::Article].exclude?(@item[:object]) - return if @params[:disable_notification] - return if !ticket - return if !signal_notifications_enabled? - return if !signal_channel - - collect_signal_recipients.each do |user| - SignalNotificationJob.perform_later( - ticket_id: ticket.id, - article_id: @item[:article_id], - user_id: user.id, - type: @item[:type], - changes: human_changes(@item[:changes], ticket, user) - ) - end - end - - private - - def ticket - @ticket ||= Ticket.find_by(id: @item[:object_id]) - end - - def article - return if !@item[:article_id] - - @article ||= begin - art = Ticket::Article.find_by(id: @item[:article_id]) - return unless art - - sender = Ticket::Article::Sender.lookup(id: art.sender_id) - if sender&.name == 'System' - return if @item[:changes].blank? && art.preferences[:notification] != true - return if art.preferences[:notification] != true - end - - art - end - end - - def current_user - @current_user ||= User.lookup(id: @item[:user_id]) || User.lookup(id: 1) - end - - def signal_notifications_enabled? - Setting.get('signal_notification_enabled') == true - end - - def signal_channel - @signal_channel ||= begin - channel_id = Setting.get('signal_notification_channel_id') - return unless channel_id - - Channel.find_by(id: channel_id, area: 'Signal::Number', active: true) - end - end - - def collect_signal_recipients - recipients = [] - - possible_recipients = possible_recipients_of_group(ticket.group_id) - - mention_users = Mention.where(mentionable_type: @item[:object], mentionable_id: @item[:object_id]).map(&:user) - mention_users.each do |user| - next if !user.group_access?(ticket.group_id, 'read') - - possible_recipients.push(user) - end - - if ticket.owner_id != 1 - possible_recipients.push(ticket.owner) - end - - possible_recipients_with_ooo = Set.new(possible_recipients) - possible_recipients.each do |user| - add_out_of_office_replacement(user, possible_recipients_with_ooo) - end - - possible_recipients_with_ooo.each do |user| - next if recipient_is_current_user?(user) - next if !user.active? - next if user_signal_uid(user).blank? - next if !user_wants_signal_for_event?(user) - - recipients.push(user) - end - - recipients.uniq(&:id) - end - - def possible_recipients_of_group(group_id) - Rails.cache.fetch("User/signal_notification/possible_recipients_of_group/#{group_id}/#{User.latest_change}", expires_in: 20.seconds) do - User.group_access(group_id, 'full').sort_by(&:login) - end - end - - def add_out_of_office_replacement(user, recipients) - replacement = user.out_of_office_agent - return unless replacement - return unless TicketPolicy.new(replacement, ticket).agent_read_access? - - recipients.add(replacement) - end - - def recipient_is_current_user?(user) - return false if @params[:interface_handle] != 'application_server' - return true if article&.updated_by_id == user.id - return true if !article && @item[:user_id] == user.id - - false - end - - def user_signal_uid(user) - user.preferences.dig('notification_config', 'signal_uid').presence - end - - def user_wants_signal_for_event?(user) - event_type = @item[:type] - return false if event_type.blank? - - event_key = case event_type - when 'create' then 'create' - when 'update', 'update.merged_into', 'update.received_merge', 'update.reaction' then 'update' - when 'reminder_reached' then 'reminder_reached' - when 'escalation', 'escalation_warning' then 'escalation' - else return false - end - - user.preferences.dig('notification_config', 'matrix', event_key, 'channel', 'signal') == true - end -end diff --git a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_create/en.txt.erb b/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_create/en.txt.erb deleted file mode 100644 index 9a12210..0000000 --- a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_create/en.txt.erb +++ /dev/null @@ -1,16 +0,0 @@ -[Ticket #<%= ticket.number %>] <%= ticket.title %> - -NEW TICKET - -Group: <%= ticket.group.name %> -Owner: <%= ticket.owner.fullname %> -State: <%= t(ticket.state.name) %> -Priority: <%= t(ticket.priority.name) %> -Customer: <%= ticket.customer.fullname %> -Created by: <%= current_user.fullname %> -<% if article -%> - -<%= article_body_preview(500) %> -<% end -%> - -<%= ticket_url %> diff --git a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_escalation/en.txt.erb b/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_escalation/en.txt.erb deleted file mode 100644 index a3fdbaa..0000000 --- a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_escalation/en.txt.erb +++ /dev/null @@ -1,11 +0,0 @@ -[Ticket #<%= ticket.number %>] <%= ticket.title %> - -ESCALATION - -Group: <%= ticket.group.name %> -Owner: <%= ticket.owner.fullname %> -State: <%= t(ticket.state.name) %> -Priority: <%= t(ticket.priority.name) %> -Customer: <%= ticket.customer.fullname %> - -<%= ticket_url %> diff --git a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_reminder_reached/en.txt.erb b/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_reminder_reached/en.txt.erb deleted file mode 100644 index e718346..0000000 --- a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_reminder_reached/en.txt.erb +++ /dev/null @@ -1,10 +0,0 @@ -[Ticket #<%= ticket.number %>] <%= ticket.title %> - -REMINDER REACHED - -Group: <%= ticket.group.name %> -Owner: <%= ticket.owner.fullname %> -State: <%= t(ticket.state.name) %> -Pending till: <%= ticket.pending_time&.strftime('%Y-%m-%d %H:%M') %> - -<%= ticket_url %> diff --git a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_update/en.txt.erb b/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_update/en.txt.erb deleted file mode 100644 index 3e87f33..0000000 --- a/packages/zammad-addon-bridge/src/app/views/signal_notification/ticket_update/en.txt.erb +++ /dev/null @@ -1,14 +0,0 @@ -[Ticket #<%= ticket.number %>] <%= ticket.title %> - -TICKET UPDATED by <%= current_user.fullname %> -<% if changes.present? -%> - -Changes: -<%= changes_summary %> -<% end -%> -<% if article -%> - -<%= article_body_preview(500) %> -<% end -%> - -<%= ticket_url_with_article %> diff --git a/packages/zammad-addon-bridge/src/config/initializers/cdr_signal.rb b/packages/zammad-addon-bridge/src/config/initializers/cdr_signal.rb index c56d0ee..f00feca 100644 --- a/packages/zammad-addon-bridge/src/config/initializers/cdr_signal.rb +++ b/packages/zammad-addon-bridge/src/config/initializers/cdr_signal.rb @@ -1,15 +1,10 @@ # frozen_string_literal: true -Rails.application.config.after_initialize do +Rails.application.config.after_initialize do class Ticket::Article include Ticket::Article::EnqueueCommunicateCdrSignalJob end - # Handle Signal group setup for split tickets - class Link - include Link::SetupSplitSignalGroup - end - icon = File.read('public/assets/images/icons/cdr_signal.svg') doc = File.open('public/assets/images/icons.svg') { |f| Nokogiri::XML(f) } if !doc.at_css('#icon-cdr-signal') @@ -20,3 +15,4 @@ Rails.application.config.after_initialize do end File.write('public/assets/images/icons.svg', doc.to_xml) end + \ No newline at end of file diff --git a/packages/zammad-addon-bridge/src/config/routes/channel_cdr_signal.rb b/packages/zammad-addon-bridge/src/config/routes/channel_cdr_signal.rb index 414056c..b7df05b 100644 --- a/packages/zammad-addon-bridge/src/config/routes/channel_cdr_signal.rb +++ b/packages/zammad-addon-bridge/src/config/routes/channel_cdr_signal.rb @@ -12,6 +12,4 @@ Zammad::Application.routes.draw do match "#{api_path}/channels_cdr_signal_enable", to: 'channels_cdr_signal#enable', via: :post match "#{api_path}/channels_cdr_signal", to: 'channels_cdr_signal#destroy', via: :delete match "#{api_path}/channels_cdr_signal_rotate_token", to: 'channels_cdr_signal#rotate_token', via: :post - match "#{api_path}/channels_cdr_signal_set_notification", to: 'channels_cdr_signal#set_notification_channel', via: :post - match "#{api_path}/channels_cdr_signal_unset_notification", to: 'channels_cdr_signal#unset_notification_channel', via: :post end diff --git a/packages/zammad-addon-bridge/src/db/addon/bridge/20260115000001_add_messaging_user_ids.rb b/packages/zammad-addon-bridge/src/db/addon/bridge/20260115000001_add_messaging_user_ids.rb deleted file mode 100644 index 2e247eb..0000000 --- a/packages/zammad-addon-bridge/src/db/addon/bridge/20260115000001_add_messaging_user_ids.rb +++ /dev/null @@ -1,123 +0,0 @@ -class AddMessagingUserIds < ActiveRecord::Migration[5.2] - def self.up - # Add WhatsApp UID column - unless column_exists?(:users, :whatsapp_uid) - add_column :users, :whatsapp_uid, :string, limit: 50 - add_index :users, :whatsapp_uid - end - User.reset_column_information - - # Add Signal UID column - unless column_exists?(:users, :signal_uid) - add_column :users, :signal_uid, :string, limit: 50 - add_index :users, :signal_uid - end - User.reset_column_information - - # Register WhatsApp UID with ObjectManager for UI - # Column name: whatsapp_uid, Display name: "WhatsApp User ID" - ObjectManager::Attribute.add( - force: true, - object: 'User', - name: 'whatsapp_uid', - display: 'WhatsApp User ID', - data_type: 'input', - data_option: { - type: 'text', - maxlength: 50, - null: true, - item_class: 'formGroup--halfSize', - }, - editable: false, - active: true, - screens: { - signup: {}, - invite_agent: {}, - invite_customer: {}, - edit: { - '-all-' => { - null: true, - }, - }, - create: { - '-all-' => { - null: true, - }, - }, - view: { - '-all-' => { - shown: true, - }, - }, - }, - to_create: false, - to_migrate: false, - to_delete: false, - position: 710, - created_by_id: 1, - updated_by_id: 1, - ) - - # Register Signal UID with ObjectManager for UI - # Column name: signal_uid, Display name: "Signal User ID" - ObjectManager::Attribute.add( - force: true, - object: 'User', - name: 'signal_uid', - display: 'Signal User ID', - data_type: 'input', - data_option: { - type: 'text', - maxlength: 50, - null: true, - item_class: 'formGroup--halfSize', - }, - editable: false, - active: true, - screens: { - signup: {}, - invite_agent: {}, - invite_customer: {}, - edit: { - '-all-' => { - null: true, - }, - }, - create: { - '-all-' => { - null: true, - }, - }, - view: { - '-all-' => { - shown: true, - }, - }, - }, - to_create: false, - to_migrate: false, - to_delete: false, - position: 720, - created_by_id: 1, - updated_by_id: 1, - ) - end - - def self.down - ObjectManager::Attribute.remove( - object: 'User', - name: 'whatsapp_uid', - ) - - ObjectManager::Attribute.remove( - object: 'User', - name: 'signal_uid', - ) - - remove_index :users, :whatsapp_uid if index_exists?(:users, :whatsapp_uid) - remove_column :users, :whatsapp_uid if column_exists?(:users, :whatsapp_uid) - - remove_index :users, :signal_uid if index_exists?(:users, :signal_uid) - remove_column :users, :signal_uid if column_exists?(:users, :signal_uid) - end -end diff --git a/packages/zammad-addon-bridge/src/db/addon/bridge/20260120000001_add_signal_notification_settings.rb b/packages/zammad-addon-bridge/src/db/addon/bridge/20260120000001_add_signal_notification_settings.rb deleted file mode 100644 index e894ae2..0000000 --- a/packages/zammad-addon-bridge/src/db/addon/bridge/20260120000001_add_signal_notification_settings.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -class AddSignalNotificationSettings < ActiveRecord::Migration[5.2] - def self.up - # Register Signal notification transaction backend - # Using 0105 to run after email notifications (0100) - Setting.create_if_not_exists( - title: 'Defines transaction backend.', - name: '0105_signal_notification', - area: 'Transaction::Backend::Async', - description: 'Defines the transaction backend to send Signal notifications.', - options: {}, - state: 'Transaction::SignalNotification', - frontend: false - ) - - # Global enable/disable for Signal notifications - Setting.create_if_not_exists( - title: 'Signal Notifications', - name: 'signal_notification_enabled', - area: 'Integration::Switch', - description: 'Enable or disable Signal notifications for agents.', - options: { - form: [ - { - display: '', - null: true, - name: 'signal_notification_enabled', - tag: 'boolean', - options: { - true => 'yes', - false => 'no', - }, - }, - ], - }, - state: false, - preferences: { - prio: 1, - permission: ['admin.integration'], - }, - frontend: true - ) - - # Which Signal channel/bot to use for sending notifications - Setting.create_if_not_exists( - title: 'Signal Notification Channel', - name: 'signal_notification_channel_id', - area: 'Integration::SignalNotification', - description: 'The Signal channel (bot) used to send notifications to agents.', - options: {}, - state: nil, - preferences: { - prio: 2, - permission: ['admin.integration'], - }, - frontend: false - ) - - end - - def self.down - # Only destroy the transaction backend registration. - # Preserve signal_notification_enabled and signal_notification_channel_id - # so admin configuration survives addon reinstalls (setup.rb runs - # uninstall + install on every container start). - Setting.find_by(name: '0105_signal_notification')&.destroy - end -end diff --git a/packages/zammad-addon-bridge/src/lib/signal_notification_sender.rb b/packages/zammad-addon-bridge/src/lib/signal_notification_sender.rb deleted file mode 100644 index 861a6c8..0000000 --- a/packages/zammad-addon-bridge/src/lib/signal_notification_sender.rb +++ /dev/null @@ -1,139 +0,0 @@ -# frozen_string_literal: true - -require 'erb' - -class SignalNotificationSender - TEMPLATE_DIR = Rails.root.join('app', 'views', 'signal_notification') - - class << self - def build_message(ticket:, article:, user:, type:, changes:) - template_name = template_for_type(type) - return if template_name.blank? - - locale = user.locale || Setting.get('locale_default') || 'en' - template_path = find_template(template_name, locale) - return if template_path.blank? - - render_template(template_path, binding_for(ticket, article, user, changes)) - end - - def send_message(channel:, recipient:, message:) - return if Rails.env.test? - return if channel.blank? - return if recipient.blank? - return if message.blank? - - api_url = channel.options['bot_endpoint'] || channel.options[:bot_endpoint] - api_token = channel.options['bot_token'] || channel.options[:bot_token] - - return if api_url.blank? || api_token.blank? - - api = CdrSignalApi.new(api_url, api_token) - api.send_message(recipient, message) - end - - private - - def template_for_type(type) - case type - when 'create' - 'ticket_create' - when 'update', 'update.merged_into', 'update.received_merge', 'update.reaction' - 'ticket_update' - when 'reminder_reached' - 'ticket_reminder_reached' - when 'escalation', 'escalation_warning' - 'ticket_escalation' - end - end - - def find_template(template_name, locale) - base_locale = locale.split('-').first - - [locale, base_locale, 'en'].uniq.each do |try_locale| - path = TEMPLATE_DIR.join(template_name, "#{try_locale}.txt.erb") - return path if File.exist?(path) - end - - nil - end - - def binding_for(ticket, article, user, changes) - TemplateContext.new( - ticket: ticket, - article: article, - user: user, - changes: changes, - config: { - http_type: Setting.get('http_type'), - fqdn: Setting.get('fqdn'), - product_name: Setting.get('product_name') - } - ).get_binding - end - - def render_template(template_path, binding) - template = File.read(template_path) - erb = ERB.new(template, trim_mode: '-') - erb.result(binding).strip - end - end - - class TemplateContext - attr_reader :ticket, :article, :recipient, :changes, :config - - def initialize(ticket:, article:, user:, changes:, config:) - @ticket = ticket - @article = article - @recipient = user - @changes = changes - @config = OpenStruct.new(config) - end - - def get_binding - binding - end - - def ticket_url - "#{config.http_type}://#{config.fqdn}/#ticket/zoom/#{ticket.id}" - end - - def ticket_url_with_article - if article - "#{ticket_url}/#{article.id}" - else - ticket_url - end - end - - def current_user - @current_user ||= User.lookup(id: ticket.updated_by_id) || User.lookup(id: 1) - end - - def changes_summary - return '' if changes.blank? - - changes.map { |key, values| "#{key}: #{values[0]} -> #{values[1]}" }.join("\n") - end - - def article_body_preview(max_length = 500) - return '' unless article - return '' if article.body.blank? - - body = article.body.to_s - body = ActionController::Base.helpers.strip_tags(body) if article.content_type&.include?('html') - body = body.gsub(/\s+/, ' ').strip - - if body.length > max_length - "#{body[0, max_length]}..." - else - body - end - end - - def t(text) - locale = recipient.locale || Setting.get('locale_default') || 'en' - Translation.translate(locale, text) - end - end -end diff --git a/packages/zammad-addon-bridge/turbo.json b/packages/zammad-addon-bridge/turbo.json deleted file mode 100644 index 109f7c8..0000000 --- a/packages/zammad-addon-bridge/turbo.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"], - "tasks": { - "build": { - "outputs": ["../../docker/zammad/addons/zammad-addon-bridge-v*.zpm"] - } - } -} diff --git a/packages/zammad-addon-common/package.json b/packages/zammad-addon-common/package.json index 2b252e4..2b3c510 100644 --- a/packages/zammad-addon-common/package.json +++ b/packages/zammad-addon-common/package.json @@ -1,6 +1,6 @@ { "name": "@link-stack/zammad-addon-common", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "", "bin": { "zpm-build": "./dist/build.js", diff --git a/packages/zammad-addon-hardening/package.json b/packages/zammad-addon-hardening/package.json index dc1373e..725472b 100644 --- a/packages/zammad-addon-hardening/package.json +++ b/packages/zammad-addon-hardening/package.json @@ -1,7 +1,7 @@ { "name": "@link-stack/zammad-addon-hardening", "displayName": "Hardening", - "version": "3.5.0-beta.1", + "version": "3.3.5", "description": "A Zammad addon that hardens a Zammad instance according to CDR's needs.", "scripts": { "build": "node '../zammad-addon-common/dist/build.js'", diff --git a/packages/zammad-addon-hardening/turbo.json b/packages/zammad-addon-hardening/turbo.json deleted file mode 100644 index 3d96279..0000000 --- a/packages/zammad-addon-hardening/turbo.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://turbo.build/schema.json", - "extends": ["//"], - "tasks": { - "build": { - "outputs": ["../../docker/zammad/addons/zammad-addon-hardening-v*.zpm"] - } - } -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3d701ab..647ef91 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,10 +35,10 @@ importers: version: 19.2.0(react@19.2.0) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@24.10.9)(typescript@5.9.3) + version: 10.9.2(@types/node@24.10.4)(typescript@5.9.3) turbo: specifier: ^2.5.8 - version: 2.7.6 + version: 2.6.3 typescript: specifier: latest version: 5.9.3 @@ -96,7 +96,7 @@ importers: version: link:../../packages/typescript-config '@types/node': specifier: ^24 - version: 24.10.9 + version: 24.10.4 '@types/pg': specifier: ^8.15.5 version: 8.16.0 @@ -123,7 +123,7 @@ importers: version: 0.27.5 pg: specifier: ^8.16.3 - version: 8.17.2 + version: 8.16.3 tsx: specifier: ^4.20.6 version: 4.21.0 @@ -136,7 +136,7 @@ importers: version: link:../../packages/typescript-config '@types/node': specifier: ^24 - version: 24.10.9 + version: 24.10.4 '@types/pg': specifier: ^8.15.5 version: 8.16.0 @@ -188,7 +188,7 @@ importers: version: 5.0.0 '@types/node': specifier: '*' - version: 24.10.9 + version: 24.10.4 dotenv-cli: specifier: ^10.0.0 version: 10.0.0 @@ -221,10 +221,10 @@ importers: version: 0.16.6(typescript@5.9.3) remeda: specifier: ^2.32.0 - version: 2.33.4 + version: 2.32.0 twilio: specifier: ^5.10.2 - version: 5.12.0 + version: 5.11.1 devDependencies: '@link-stack/eslint-config': specifier: workspace:* @@ -303,7 +303,7 @@ importers: version: 7.4.0(graphql@16.12.0) ioredis: specifier: ^5.8.1 - version: 5.9.2 + version: 5.8.2 mui-chips-input: specifier: ^6.0.0 version: 6.0.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -337,7 +337,7 @@ importers: version: link:../../packages/eslint-config '@types/node': specifier: ^24.7.0 - version: 24.10.9 + version: 24.10.4 '@types/react': specifier: 19.2.2 version: 19.2.2 @@ -355,7 +355,7 @@ importers: version: 0.27.5 pg: specifier: ^8.16.3 - version: 8.17.2 + version: 8.16.3 devDependencies: '@link-stack/eslint-config': specifier: workspace:* @@ -405,7 +405,7 @@ importers: devDependencies: '@types/node': specifier: ^24.7.0 - version: 24.10.9 + version: 24.10.4 '@types/react': specifier: 19.2.2 version: 19.2.2 @@ -420,16 +420,16 @@ importers: dependencies: '@babel/eslint-parser': specifier: 7.28.4 - version: 7.28.4(@babel/core@7.28.6)(eslint@9.39.2) + version: 7.28.4(@babel/core@7.28.5)(eslint@9.39.2) '@rushstack/eslint-patch': specifier: ^1.13.0 version: 1.15.0 '@typescript-eslint/eslint-plugin': specifier: ^8.46.0 - version: 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + version: 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) '@typescript-eslint/parser': specifier: ^8.46.0 - version: 8.54.0(eslint@9.39.2)(typescript@5.9.3) + version: 8.50.0(eslint@9.39.2)(typescript@5.9.3) eslint-config-prettier: specifier: ^10.1.8 version: 10.1.8(eslint@9.39.2) @@ -441,10 +441,10 @@ importers: version: 3.2.0(eslint@9.39.2) eslint-plugin-import: specifier: ^2.32.0 - version: 2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2) + version: 2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2) eslint-plugin-jest: specifier: ^29.0.1 - version: 29.12.1(@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(jest@30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.5.0(@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(jest@30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)))(typescript@5.9.3) eslint-plugin-promise: specifier: ^7.2.1 version: 7.2.1(eslint@9.39.2) @@ -457,7 +457,7 @@ importers: version: 9.39.2 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + version: 30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -469,7 +469,7 @@ importers: version: 30.0.0 jest: specifier: ^30.2.0 - version: 30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + version: 30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) jest-junit: specifier: ^16.0.0 version: 16.0.0 @@ -478,7 +478,7 @@ importers: dependencies: pino: specifier: ^10.0.0 - version: 10.3.0 + version: 10.1.0 pino-pretty: specifier: ^13.1.1 version: 13.1.3 @@ -491,7 +491,7 @@ importers: version: link:../typescript-config '@types/node': specifier: ^24.7.0 - version: 24.10.9 + version: 24.10.4 eslint: specifier: ^9.37.0 version: 9.39.2 @@ -512,10 +512,10 @@ importers: version: link:../typescript-config '@openapitools/openapi-generator-cli': specifier: ^2.24.0 - version: 2.28.0(@types/node@24.10.9) + version: 2.25.2(@types/node@24.10.4) '@types/node': specifier: ^24 - version: 24.10.9 + version: 24.10.4 typescript: specifier: ^5 version: 5.9.3 @@ -548,7 +548,7 @@ importers: devDependencies: '@types/node': specifier: ^24.7.0 - version: 24.10.9 + version: 24.10.4 '@types/react': specifier: 19.2.2 version: 19.2.2 @@ -576,7 +576,7 @@ importers: devDependencies: '@types/node': specifier: ^24.7.0 - version: 24.10.9 + version: 24.10.4 typescript: specifier: ^5 version: 5.9.3 @@ -611,16 +611,16 @@ packages: peerDependencies: kysely: ^0.27.5 - '@babel/code-frame@7.28.6': - resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.6': - resolution: {integrity: sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.6': - resolution: {integrity: sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==} + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} engines: {node: '>=6.9.0'} '@babel/eslint-parser@7.28.4': @@ -630,30 +630,30 @@ packages: '@babel/core': ^7.11.0 eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - '@babel/generator@7.28.6': - resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.28.6': - resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} '@babel/helper-globals@7.28.0': resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.6': - resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.27.1': @@ -668,12 +668,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.6': - resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -698,8 +698,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -714,8 +714,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.28.6': - resolution: {integrity: sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==} + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -762,43 +762,46 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.28.6': - resolution: {integrity: sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==} + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.28.4': + resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} - '@babel/template@7.28.6': - resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.6': - resolution: {integrity: sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.6': - resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - '@borewit/text-codec@0.2.1': - resolution: {integrity: sha512-k7vvKPbf7J2fZ5klGRD9AeKfUvojuZIQ3BT5u7Jfv+puwXkUBUT5PVyMDfJZpy30CBDXGMgw7fguK/lpOMBvgw==} + '@borewit/text-codec@0.1.1': + resolution: {integrity: sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==} - '@cacheable/memory@2.0.7': - resolution: {integrity: sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==} + '@borewit/text-codec@0.2.0': + resolution: {integrity: sha512-X999CKBxGwX8wW+4gFibsbiNdwqmdQEXmUejIWaIqdrHBgS5ARIOOeyiQbHjP9G58xVEPcuvP6VwwH3A0OFTOA==} + + '@cacheable/memory@2.0.6': + resolution: {integrity: sha512-7e8SScMocHxcAb8YhtkbMhGG+EKLRIficb1F5sjvhSYsWTZGxvg4KIDp8kgxnV2PUJ3ddPe6J9QESjKvBWRDkg==} '@cacheable/node-cache@1.7.6': resolution: {integrity: sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==} engines: {node: '>=18'} - '@cacheable/utils@2.3.3': - resolution: {integrity: sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==} + '@cacheable/utils@2.3.2': + resolution: {integrity: sha512-8kGE2P+HjfY8FglaOiW+y8qxcaQAfAhVML+i66XJR3YX5FtyDqn6Txctr3K2FrbxLKixRRYYBWMbuGciOhYNDg==} '@chatscope/chat-ui-kit-react@2.1.1': resolution: {integrity: sha512-rCtE9abdmAbBDkAAUYBC1TDTBMZHquqFIZhADptAfHcJ8z8W3XH/z/ZuwBSJXtzi6h1mwCNc3tBmm1A2NLGhNg==} @@ -814,11 +817,11 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/core@1.8.1': - resolution: {integrity: sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} @@ -1041,8 +1044,8 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.9.1': - resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -1392,8 +1395,8 @@ packages: '@types/node': optional: true - '@ioredis/commands@1.5.0': - resolution: {integrity: sha512-eUgLqrMf8nJkZxT24JvVRrQya1vZkQh8BBeYNwGDqa5I0VUi8ACx7uFvAaLxintokpTenkK6DASvo/bvNbBGow==} + '@ioredis/commands@1.4.0': + resolution: {integrity: sha512-aFT2yemJJo+TZCmieA7qnYGQooOS7QfNmYrzGtsYd3g9j5iDP8AimYYAesf79ohjbLG12XxC4nG5DyEnC88AsQ==} '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} @@ -1516,11 +1519,11 @@ packages: '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@keyv/bigmap@1.3.1': - resolution: {integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==} + '@keyv/bigmap@1.3.0': + resolution: {integrity: sha512-KT01GjzV6AQD5+IYrcpoYLkCu1Jod3nau1Z7EsEuViO3TZGRacSbO9MfHmbJ1WaOXFtWLxPVj169cn2WNKPkIg==} engines: {node: '>= 18'} peerDependencies: - keyv: ^5.6.0 + keyv: ^5.5.4 '@keyv/serialize@1.1.1': resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==} @@ -1628,8 +1631,8 @@ packages: '@types/react': optional: true - '@mui/types@7.4.10': - resolution: {integrity: sha512-0+4mSjknSu218GW3isRqoxKRTOrTLd/vHi/7UC4+wZcUrOAqD9kRk7UQRL1mcrzqRoe7s3UT6rsRpbLkW5mHpQ==} + '@mui/types@7.4.9': + resolution: {integrity: sha512-dNO8Z9T2cujkSIaCnWwprfeKmTWh97cnjkgmpFJ2sbfXLx8SMZijCYHOtP/y5nnUb/Rm2omxbDMmtUoSaUtKaw==} peerDependencies: '@types/react': 19.2.2 peerDependenciesMeta: @@ -1646,8 +1649,8 @@ packages: '@types/react': optional: true - '@mui/utils@7.3.7': - resolution: {integrity: sha512-+YjnjMRnyeTkWnspzoxRdiSOgkrcpTikhNPoxOZW0APXx+urHtUoXJ9lbtCZRCA5a4dg5gSbd19alL1DvRs5fg==} + '@mui/utils@7.3.6': + resolution: {integrity: sha512-jn+Ba02O6PiFs7nKva8R2aJJ9kJC+3kQ2R0BbKNY3KQQ36Qng98GnPRFTlbwYTdMD6hLEBKaMLUktyg/rTfd2w==} engines: {node: '>=14.0.0'} peerDependencies: '@types/react': 19.2.2 @@ -1784,8 +1787,8 @@ packages: axios: ^1.3.1 rxjs: ^7.0.0 - '@nestjs/common@11.1.12': - resolution: {integrity: sha512-v6U3O01YohHO+IE3EIFXuRuu3VJILWzyMmSYZXpyBbnp0hk0mFyHxK2w3dF4I5WnbwiRbWlEXdeXFvPQ7qaZzw==} + '@nestjs/common@11.1.9': + resolution: {integrity: sha512-zDntUTReRbAThIfSp3dQZ9kKqI+LjgLp5YZN5c1bgNRDuoeLySAoZg46Bg1a+uV8TMgIRziHocglKGNzr6l+bQ==} peerDependencies: class-transformer: '>=0.4.1' class-validator: '>=0.13.2' @@ -1797,8 +1800,8 @@ packages: class-validator: optional: true - '@nestjs/core@11.1.12': - resolution: {integrity: sha512-97DzTYMf5RtGAVvX1cjwpKRiCUpkeQ9CCzSAenqkAhOmNVVFaApbhuw+xrDt13rsCa2hHVOYPrV4dBgOYMJjsA==} + '@nestjs/core@11.1.9': + resolution: {integrity: sha512-a00B0BM4X+9z+t3UxJqIZlemIwCQdYoPKrMcM+ky4z3pkqqG1eTWexjs+YXpGObnLnjtMPVKWlcZHp3adDYvUw==} engines: {node: '>= 20'} peerDependencies: '@nestjs/common': ^11.0.0 @@ -1879,8 +1882,8 @@ packages: engines: {node: '>=8.0.0', npm: '>=5.0.0'} hasBin: true - '@openapitools/openapi-generator-cli@2.28.0': - resolution: {integrity: sha512-eWLMHYGxaRSO8NCIovYeRiT1JFWjVzcLdxPpaCGUiIWOFUW4gnPHOppPp4x0adFRji6dTezg9L4Cm4tHSaftgQ==} + '@openapitools/openapi-generator-cli@2.25.2': + resolution: {integrity: sha512-TXElbW1NXCy0EECXiO5AD2ZzT1dmaCs41Z8t3pBUGaJf8zgF/Lm0P6GRhVEpw29iHBNjZcy8nrgQ1acUfuCdng==} engines: {node: '>=16'} hasBin: true @@ -1931,128 +1934,113 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - '@rollup/rollup-android-arm-eabi@4.57.0': - resolution: {integrity: sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==} + '@rollup/rollup-android-arm-eabi@4.53.5': + resolution: {integrity: sha512-iDGS/h7D8t7tvZ1t6+WPK04KD0MwzLZrG0se1hzBjSi5fyxlsiggoJHwh18PCFNn7tG43OWb6pdZ6Y+rMlmyNQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.57.0': - resolution: {integrity: sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==} + '@rollup/rollup-android-arm64@4.53.5': + resolution: {integrity: sha512-wrSAViWvZHBMMlWk6EJhvg8/rjxzyEhEdgfMMjREHEq11EtJ6IP6yfcCH57YAEca2Oe3FNCE9DSTgU70EIGmVw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.57.0': - resolution: {integrity: sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==} + '@rollup/rollup-darwin-arm64@4.53.5': + resolution: {integrity: sha512-S87zZPBmRO6u1YXQLwpveZm4JfPpAa6oHBX7/ghSiGH3rz/KDgAu1rKdGutV+WUI6tKDMbaBJomhnT30Y2t4VQ==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.57.0': - resolution: {integrity: sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==} + '@rollup/rollup-darwin-x64@4.53.5': + resolution: {integrity: sha512-YTbnsAaHo6VrAczISxgpTva8EkfQus0VPEVJCEaboHtZRIb6h6j0BNxRBOwnDciFTZLDPW5r+ZBmhL/+YpTZgA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.57.0': - resolution: {integrity: sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==} + '@rollup/rollup-freebsd-arm64@4.53.5': + resolution: {integrity: sha512-1T8eY2J8rKJWzaznV7zedfdhD1BqVs1iqILhmHDq/bqCUZsrMt+j8VCTHhP0vdfbHK3e1IQ7VYx3jlKqwlf+vw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.57.0': - resolution: {integrity: sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==} + '@rollup/rollup-freebsd-x64@4.53.5': + resolution: {integrity: sha512-sHTiuXyBJApxRn+VFMaw1U+Qsz4kcNlxQ742snICYPrY+DDL8/ZbaC4DVIB7vgZmp3jiDaKA0WpBdP0aqPJoBQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.57.0': - resolution: {integrity: sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': + resolution: {integrity: sha512-dV3T9MyAf0w8zPVLVBptVlzaXxka6xg1f16VAQmjg+4KMSTWDvhimI/Y6mp8oHwNrmnmVl9XxJ/w/mO4uIQONA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.57.0': - resolution: {integrity: sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==} + '@rollup/rollup-linux-arm-musleabihf@4.53.5': + resolution: {integrity: sha512-wIGYC1x/hyjP+KAu9+ewDI+fi5XSNiUi9Bvg6KGAh2TsNMA3tSEs+Sh6jJ/r4BV/bx/CyWu2ue9kDnIdRyafcQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.57.0': - resolution: {integrity: sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==} + '@rollup/rollup-linux-arm64-gnu@4.53.5': + resolution: {integrity: sha512-Y+qVA0D9d0y2FRNiG9oM3Hut/DgODZbU9I8pLLPwAsU0tUKZ49cyV1tzmB/qRbSzGvY8lpgGkJuMyuhH7Ma+Vg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.57.0': - resolution: {integrity: sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==} + '@rollup/rollup-linux-arm64-musl@4.53.5': + resolution: {integrity: sha512-juaC4bEgJsyFVfqhtGLz8mbopaWD+WeSOYr5E16y+1of6KQjc0BpwZLuxkClqY1i8sco+MdyoXPNiCkQou09+g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.57.0': - resolution: {integrity: sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==} + '@rollup/rollup-linux-loong64-gnu@4.53.5': + resolution: {integrity: sha512-rIEC0hZ17A42iXtHX+EPJVL/CakHo+tT7W0pbzdAGuWOt2jxDFh7A/lRhsNHBcqL4T36+UiAgwO8pbmn3dE8wA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-musl@4.57.0': - resolution: {integrity: sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==} - cpu: [loong64] - os: [linux] - - '@rollup/rollup-linux-ppc64-gnu@4.57.0': - resolution: {integrity: sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==} + '@rollup/rollup-linux-ppc64-gnu@4.53.5': + resolution: {integrity: sha512-T7l409NhUE552RcAOcmJHj3xyZ2h7vMWzcwQI0hvn5tqHh3oSoclf9WgTl+0QqffWFG8MEVZZP1/OBglKZx52Q==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-musl@4.57.0': - resolution: {integrity: sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==} - cpu: [ppc64] - os: [linux] - - '@rollup/rollup-linux-riscv64-gnu@4.57.0': - resolution: {integrity: sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==} + '@rollup/rollup-linux-riscv64-gnu@4.53.5': + resolution: {integrity: sha512-7OK5/GhxbnrMcxIFoYfhV/TkknarkYC1hqUw1wU2xUN3TVRLNT5FmBv4KkheSG2xZ6IEbRAhTooTV2+R5Tk0lQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.57.0': - resolution: {integrity: sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==} + '@rollup/rollup-linux-riscv64-musl@4.53.5': + resolution: {integrity: sha512-GwuDBE/PsXaTa76lO5eLJTyr2k8QkPipAyOrs4V/KJufHCZBJ495VCGJol35grx9xryk4V+2zd3Ri+3v7NPh+w==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.57.0': - resolution: {integrity: sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==} + '@rollup/rollup-linux-s390x-gnu@4.53.5': + resolution: {integrity: sha512-IAE1Ziyr1qNfnmiQLHBURAD+eh/zH1pIeJjeShleII7Vj8kyEm2PF77o+lf3WTHDpNJcu4IXJxNO0Zluro8bOw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.57.0': - resolution: {integrity: sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==} + '@rollup/rollup-linux-x64-gnu@4.53.5': + resolution: {integrity: sha512-Pg6E+oP7GvZ4XwgRJBuSXZjcqpIW3yCBhK4BcsANvb47qMvAbCjR6E+1a/U2WXz1JJxp9/4Dno3/iSJLcm5auw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.57.0': - resolution: {integrity: sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==} + '@rollup/rollup-linux-x64-musl@4.53.5': + resolution: {integrity: sha512-txGtluxDKTxaMDzUduGP0wdfng24y1rygUMnmlUJ88fzCCULCLn7oE5kb2+tRB+MWq1QDZT6ObT5RrR8HFRKqg==} cpu: [x64] os: [linux] - '@rollup/rollup-openbsd-x64@4.57.0': - resolution: {integrity: sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==} - cpu: [x64] - os: [openbsd] - - '@rollup/rollup-openharmony-arm64@4.57.0': - resolution: {integrity: sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==} + '@rollup/rollup-openharmony-arm64@4.53.5': + resolution: {integrity: sha512-3DFiLPnTxiOQV993fMc+KO8zXHTcIjgaInrqlG8zDp1TlhYl6WgrOHuJkJQ6M8zHEcntSJsUp1XFZSY8C1DYbg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.57.0': - resolution: {integrity: sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==} + '@rollup/rollup-win32-arm64-msvc@4.53.5': + resolution: {integrity: sha512-nggc/wPpNTgjGg75hu+Q/3i32R00Lq1B6N1DO7MCU340MRKL3WZJMjA9U4K4gzy3dkZPXm9E1Nc81FItBVGRlA==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.57.0': - resolution: {integrity: sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==} + '@rollup/rollup-win32-ia32-msvc@4.53.5': + resolution: {integrity: sha512-U/54pTbdQpPLBdEzCT6NBCFAfSZMvmjr0twhnD9f4EIvlm9wy3jjQ38yQj1AGznrNO65EWQMgm/QUjuIVrYF9w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.57.0': - resolution: {integrity: sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==} + '@rollup/rollup-win32-x64-gnu@4.53.5': + resolution: {integrity: sha512-2NqKgZSuLH9SXBBV2dWNRCZmocgSOx8OJSdpRaEcRlIfX8YrKxUT6z0F1NpvDVhOsl190UFTRh2F2WDWWCYp3A==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.57.0': - resolution: {integrity: sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==} + '@rollup/rollup-win32-x64-msvc@4.53.5': + resolution: {integrity: sha512-JRpZUhCfhZ4keB5v0fe02gQJy05GqboPOaxvjugW04RLSYYoB/9t2lx2u/tMs/Na/1NXfY8QYjgRljRpN+MjTQ==} cpu: [x64] os: [win32] @@ -2062,8 +2050,8 @@ packages: '@rushstack/eslint-patch@1.15.0': resolution: {integrity: sha512-ojSshQPKwVvSMR8yT2L/QtUkV5SXi/IfDiJ4/8d6UbTPjiHVmxZzUAzGD8Tzks1b9+qQkZa0isUOvYObedITaw==} - '@sinclair/typebox@0.34.48': - resolution: {integrity: sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==} + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -2074,6 +2062,10 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + '@tokenizer/inflate@0.3.1': + resolution: {integrity: sha512-4oeoZEBQdLdt5WmP/hx1KZ6D3/Oid/0cUb2nk4F0pTDAWy+KCH3/EnAkZF/bvckWo8I33EqBm01lIPgmgc8rCA==} + engines: {node: '>=18'} + '@tokenizer/inflate@0.4.1': resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==} engines: {node: '>=18'} @@ -2162,11 +2154,11 @@ packages: '@types/node@10.17.60': resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - '@types/node@22.19.7': - resolution: {integrity: sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==} + '@types/node@22.19.3': + resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} - '@types/node@24.10.9': - resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==} + '@types/node@24.10.4': + resolution: {integrity: sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==} '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -2202,63 +2194,63 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.54.0': - resolution: {integrity: sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==} + '@typescript-eslint/eslint-plugin@8.50.0': + resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.54.0 + '@typescript-eslint/parser': ^8.50.0 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.54.0': - resolution: {integrity: sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==} + '@typescript-eslint/parser@8.50.0': + resolution: {integrity: sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.54.0': - resolution: {integrity: sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==} + '@typescript-eslint/project-service@8.50.0': + resolution: {integrity: sha512-Cg/nQcL1BcoTijEWyx4mkVC56r8dj44bFDvBdygifuS20f3OZCHmFbjF34DPSi07kwlFvqfv/xOLnJ5DquxSGQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.54.0': - resolution: {integrity: sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==} + '@typescript-eslint/scope-manager@8.50.0': + resolution: {integrity: sha512-xCwfuCZjhIqy7+HKxBLrDVT5q/iq7XBVBXLn57RTIIpelLtEIZHXAF/Upa3+gaCpeV1NNS5Z9A+ID6jn50VD4A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.54.0': - resolution: {integrity: sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==} + '@typescript-eslint/tsconfig-utils@8.50.0': + resolution: {integrity: sha512-vxd3G/ybKTSlm31MOA96gqvrRGv9RJ7LGtZCn2Vrc5htA0zCDvcMqUkifcjrWNNKXHUU3WCkYOzzVSFBd0wa2w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.54.0': - resolution: {integrity: sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==} + '@typescript-eslint/type-utils@8.50.0': + resolution: {integrity: sha512-7OciHT2lKCewR0mFoBrvZJ4AXTMe/sYOe87289WAViOocEmDjjv8MvIOT2XESuKj9jp8u3SZYUSh89QA4S1kQw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.54.0': - resolution: {integrity: sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==} + '@typescript-eslint/types@8.50.0': + resolution: {integrity: sha512-iX1mgmGrXdANhhITbpp2QQM2fGehBse9LbTf0sidWK6yg/NE+uhV5dfU1g6EYPlcReYmkE9QLPq/2irKAmtS9w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.54.0': - resolution: {integrity: sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==} + '@typescript-eslint/typescript-estree@8.50.0': + resolution: {integrity: sha512-W7SVAGBR/IX7zm1t70Yujpbk+zdPq/u4soeFSknWFdXIFuWsBGBOUu/Tn/I6KHSKvSh91OiMuaSnYp3mtPt5IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.54.0': - resolution: {integrity: sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==} + '@typescript-eslint/utils@8.50.0': + resolution: {integrity: sha512-87KgUXET09CRjGCi2Ejxy3PULXna63/bMYv72tCAlDJC3Yqwln0HiFJ3VJMst2+mEtNtZu5oFvX4qJGjKsnAgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.54.0': - resolution: {integrity: sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==} + '@typescript-eslint/visitor-keys@8.50.0': + resolution: {integrity: sha512-Xzmnb58+Db78gT/CCj/PVCvK+zxbnsw6F+O1oheYszJbBSdEjVhQi3C/Xttzxgi/GLmpvOggRs1RFpiJ8+c34Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.0': @@ -2499,9 +2491,6 @@ packages: axios@1.13.2: resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} - axios@1.13.4: - resolution: {integrity: sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==} - babel-jest@30.2.0: resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2537,12 +2526,12 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.18: - resolution: {integrity: sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==} + baseline-browser-mapping@2.9.10: + resolution: {integrity: sha512-2VIKvDx8Z1a9rTB2eCkdPE5nSe28XnA+qivGnWHoB40hMMt/h1hSz0960Zqsn6ZyxWXUie0EBdElKv8may20AA==} hasBin: true - basic-ftp@5.1.0: - resolution: {integrity: sha512-RkaJzeJKDbaDWTIPiJwubyljaEPwpVWkm9Rt5h9Nd6h7tEXTJ3VB4qxdZBioV7JO5yLUaOKwz7vDOzlncUsegw==} + basic-ftp@5.0.5: + resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==} engines: {node: '>=10.0.0'} bl@4.1.0: @@ -2595,8 +2584,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - cacheable@2.3.2: - resolution: {integrity: sha512-w+ZuRNmex9c1TR9RcsxbfTKCjSL0rh1WA5SABbrWprIHeNBdmyQLSYonlDy9gpD+63XT8DgZ/wNh1Smvc9WnJA==} + cacheable@2.3.1: + resolution: {integrity: sha512-yr+FSHWn1ZUou5LkULX/S+jhfgfnLbuKQjE40tyEd4fxGZVMbBL5ifno0J0OauykS8UiCSgHi+DV/YD+rjFxFg==} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} @@ -2622,8 +2611,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001766: - resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==} + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -2654,8 +2643,8 @@ packages: resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} engines: {node: '>=8'} - cjs-module-lexer@2.2.0: - resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} + cjs-module-lexer@2.1.1: + resolution: {integrity: sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==} classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -2770,8 +2759,8 @@ packages: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} - core-js-compat@3.48.0: - resolution: {integrity: sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q==} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -2897,8 +2886,8 @@ packages: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - diff@4.0.4: - resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} engines: {node: '>=0.3.1'} doctrine@2.1.0: @@ -2961,8 +2950,8 @@ packages: ecdsa-sig-formatter@1.0.11: resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} - electron-to-chromium@1.5.279: - resolution: {integrity: sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -3100,8 +3089,8 @@ packages: '@typescript-eslint/parser': optional: true - eslint-plugin-jest@29.12.1: - resolution: {integrity: sha512-Rxo7r4jSANMBkXLICJKS0gjacgyopfNAsoS0e3R9AHnjoKuQOaaPfmsDJPi8UWwygI099OV/K/JhpYRVkxD4AA==} + eslint-plugin-jest@29.5.0: + resolution: {integrity: sha512-DAi9H8xN/TUuNOt+xDP1RqpCJLsSxBb5u1zXSpCyp0VAWGL8MBAg5t7/Dk+76iX7d1LhWu4DDH77IQNUolLDyg==} engines: {node: ^20.12.0 || ^22.0.0 || >=24.0.0} peerDependencies: '@typescript-eslint/eslint-plugin': ^8.0.0 @@ -3164,8 +3153,8 @@ packages: engines: {node: '>=4'} hasBin: true - esquery@1.7.0: - resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} esrecurse@4.3.0: @@ -3223,6 +3212,9 @@ packages: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -3231,8 +3223,12 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - file-type@21.3.0: - resolution: {integrity: sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==} + file-type@21.1.0: + resolution: {integrity: sha512-boU4EHmP3JXkwDo4uhyBhTt5pPstxB6eEXKJBu2yu2l7aAMMm7QQYQEzssJmKReZYrFdFOJS8koVo6bXIBGDqA==} + engines: {node: '>=20'} + + file-type@21.1.1: + resolution: {integrity: sha512-ifJXo8zUqbQ/bLbl9sFoqHNTNWbnPY1COImFfM6CCy7z+E+jC1eY9YfOKkx0fckIg+VljAy2/87T61fp0+eEkg==} engines: {node: '>=20'} fill-range@7.1.1: @@ -3290,8 +3286,8 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - fs-extra@11.3.3: - resolution: {integrity: sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==} + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} fs.realpath@1.0.0: @@ -3436,8 +3432,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hashery@1.4.0: - resolution: {integrity: sha512-Wn2i1In6XFxl8Az55kkgnFRiAlIAushzh26PTjL2AKtQcEfXrcLa7Hn5QOWGZEf3LU057P9TwwZjFyxfS1VuvQ==} + hashery@1.3.0: + resolution: {integrity: sha512-fWltioiy5zsSAs9ouEnvhsVJeAXRybGCNNv0lvzpzNOSDbULXRy7ivFWwCCv4I5Am6kSo75hmbsCduOoc2/K4w==} engines: {node: '>=20'} hasown@2.0.2: @@ -3450,8 +3446,8 @@ packages: hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - hookified@1.15.0: - resolution: {integrity: sha512-51w+ZZGt7Zw5q7rM3nC4t3aLn/xvKDETsXqMczndvwyVQhAHfUmUuFBRFcos8Iyebtk7OAE9dL26wFNzZVVOkw==} + hookified@1.14.0: + resolution: {integrity: sha512-pi1ynXIMFx/uIIwpWJ/5CEtOHLGtnUB0WhGeeYT+fKcQ+WCQbm3/rrkAXnpfph++PgepNqPdTC2WTj8A6k6zoQ==} html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -3479,8 +3475,8 @@ packages: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} - iconv-lite@0.7.2: - resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + iconv-lite@0.7.1: + resolution: {integrity: sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==} engines: {node: '>=0.10.0'} ieee754@1.2.1: @@ -3530,8 +3526,8 @@ packages: resolution: {integrity: sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==} engines: {node: '>=10.13.0'} - ioredis@5.9.2: - resolution: {integrity: sha512-tAAg/72/VxOUW7RQSX1pIxJVucYKcjFjfvj60L57jrZpYCHC3XN0WCQ3sNYL4Gmvv+7GPvTAjc+KSdeNuE8oWQ==} + ioredis@5.8.2: + resolution: {integrity: sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q==} engines: {node: '>=12.22.0'} ip-address@10.1.0: @@ -3908,8 +3904,8 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - keyv@5.6.0: - resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + keyv@5.5.5: + resolution: {integrity: sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==} kysely@0.27.5: resolution: {integrity: sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==} @@ -3980,8 +3976,8 @@ packages: lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} @@ -4000,8 +3996,8 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.2.5: - resolution: {integrity: sha512-vFrFJkWtJvJnD5hg+hJvVE8Lh/TcMzKnTgCWmtBipwI5yLX/iX+5UB2tfuyODF5E7k9xEzMdYgGqaSb1c0c5Yw==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} lru-cache@5.1.1: @@ -4105,8 +4101,8 @@ packages: multipipe@1.0.2: resolution: {integrity: sha512-6uiC9OvY71vzSGX8lZvSqscE7ft9nPupJ8fMjrCNRAUy2LREUW42UL+V/NTrogr6rFgRydUrCX4ZitfpSNkSCQ==} - music-metadata@11.11.0: - resolution: {integrity: sha512-OTlsv/FiCr+c4+fC6t9j/GTC/m1KKc3QtOTYHVEvvGLDLpPdtgf32pB7JXJ/Xi8qdIxwwh2PR8J/1t0QL1BxWQ==} + music-metadata@11.10.3: + resolution: {integrity: sha512-j0g/x4cNNZW6I5gdcPAY+GFkJY9WHTpkFDMBJKQLxJQyvSfQbXm57fTE3haGFFuOzCgtsTd4Plwc49Sn9RacDQ==} engines: {node: '>=18'} mute-stream@0.0.8: @@ -4350,30 +4346,30 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pg-cloudflare@1.3.0: - resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} + pg-cloudflare@1.2.7: + resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} - pg-connection-string@2.10.1: - resolution: {integrity: sha512-iNzslsoeSH2/gmDDKiyMqF64DATUCWj3YJ0wP14kqcsf2TUklwimd+66yYojKwZCA7h2yRNLGug71hCBA2a4sw==} + pg-connection-string@2.9.1: + resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} engines: {node: '>=4.0.0'} - pg-pool@3.11.0: - resolution: {integrity: sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==} + pg-pool@3.10.1: + resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==} peerDependencies: pg: '>=8.0' - pg-protocol@1.11.0: - resolution: {integrity: sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g==} + pg-protocol@1.10.3: + resolution: {integrity: sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==} pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg@8.17.2: - resolution: {integrity: sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw==} + pg@8.16.3: + resolution: {integrity: sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==} engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -4405,11 +4401,11 @@ packages: resolution: {integrity: sha512-ttXRkkOz6WWC95KeY9+xxWL6AtImwbyMHrL1mSwqwW9u+vLp/WIElvHvCSDg0xO/Dzrggz1zv3rN5ovTRVowKg==} hasBin: true - pino-std-serializers@7.1.0: - resolution: {integrity: sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==} + pino-std-serializers@7.0.0: + resolution: {integrity: sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==} - pino@10.3.0: - resolution: {integrity: sha512-0GNPNzHXBKw6U/InGe79A3Crzyk9bcSyObF9/Gfo9DLEf5qj5RF50RSjsu0W1rZ6ZqRGdzDFCRBQvi9/rSGPtA==} + pino@10.1.0: + resolution: {integrity: sha512-0zZC2ygfdqvqK8zJIr1e+wT1T/L+LF6qvqvbzEQ6tiMAoTqEVK9a1K3YRu8HEUvGEvNqZyPJTtb2sNIoTkB83w==} hasBin: true pino@9.14.0: @@ -4486,8 +4482,8 @@ packages: preact@10.24.3: resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} - preact@10.28.2: - resolution: {integrity: sha512-lbteaWGzGHdlIuiJ0l2Jq454m6kcpI1zNje6d8MlGAFlYvP2GO4ibnat7P74Esfz4sPTdM6UxtTwh/d3pwM9JA==} + preact@10.28.0: + resolution: {integrity: sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -4537,15 +4533,15 @@ packages: pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} - qified@0.6.0: - resolution: {integrity: sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==} + qified@0.5.3: + resolution: {integrity: sha512-kXuQdQTB6oN3KhI6V4acnBSZx8D2I4xzZvn9+wFLLFCoBNQY/sFnCW6c43OL7pOQ2HvGV4lnWIXNmgfp7cTWhQ==} engines: {node: '>=20'} qr.js@0.0.0: resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} - qs@6.14.1: - resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} querystring@0.2.0: @@ -4577,8 +4573,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-is@19.2.4: - resolution: {integrity: sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==} + react-is@19.2.3: + resolution: {integrity: sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==} react-polyglot@0.7.2: resolution: {integrity: sha512-d/075aofJ4of9wOSBewl+ViFkkM0L1DgE3RVDOXrHZ92w4o2643sTQJ6lSPw8wsJWFmlB/3Pvwm0UbGNvLfPBw==} @@ -4646,8 +4642,8 @@ packages: resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true - remeda@2.33.4: - resolution: {integrity: sha512-ygHswjlc/opg2VrtiYvUOPLjxjtdKvjGz1/plDhkG66hjNjFr1xmfrs2ClNFo/E6TyUFiwYNh53bKV26oBoMGQ==} + remeda@2.32.0: + resolution: {integrity: sha512-BZx9DsT4FAgXDTOdgJIc5eY6ECIXMwtlSPQoPglF20ycSWigttDDe88AozEsPPT4OWk5NujroGSBC1phw5uU+w==} require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -4680,8 +4676,8 @@ packages: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} - rollup@4.57.0: - resolution: {integrity: sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==} + rollup@4.53.5: + resolution: {integrity: sha512-iTNAbFSlRpcHeeWu73ywU/8KuU/LZmNCSxp6fjQkJBD3ivUb8tpDrXhIxEzA05HlYMEwmtaUnb3RP+YNv162OQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -4722,7 +4718,6 @@ packages: scmp@2.1.0: resolution: {integrity: sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==} - deprecated: Just use Node.js's crypto.timingSafeEqual() secure-json-parse@4.1.0: resolution: {integrity: sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==} @@ -4945,8 +4940,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - synckit@0.11.12: - resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} engines: {node: ^14.18.0 || >=16.0.0} test-exclude@6.0.0: @@ -4963,10 +4958,6 @@ packages: thread-stream@3.1.0: resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==} - thread-stream@4.0.0: - resolution: {integrity: sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA==} - engines: {node: '>=20'} - through2@0.4.2: resolution: {integrity: sha512-45Llu+EwHKtAZYTPPVn3XZHBgakWMN3rokhEv5hu596XP+cNgplMg+Gj+1nmAvj+L0K7+N49zBKx5rah5u0QIQ==} @@ -4987,8 +4978,8 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - token-types@6.1.2: - resolution: {integrity: sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==} + token-types@6.1.1: + resolution: {integrity: sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==} engines: {node: '>=14.16'} tr46@0.0.3: @@ -4998,8 +4989,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@2.4.0: - resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' @@ -5051,42 +5042,42 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@2.7.6: - resolution: {integrity: sha512-bYu0qnWju2Ha3EbIkPCk1SMLT3sltKh1P/Jy5FER6BmH++H5z+T5MHh3W1Xoers9rk4N1VdKvog9FO1pxQyjhw==} + turbo-darwin-64@2.6.3: + resolution: {integrity: sha512-BlJJDc1CQ7SK5Y5qnl7AzpkvKSnpkfPmnA+HeU/sgny3oHZckPV2776ebO2M33CYDSor7+8HQwaodY++IINhYg==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.7.6: - resolution: {integrity: sha512-KCxTf3Y1hgNLYIWRLw8bwH8Zie9RyCGoxAlXYsCBI/YNqBSR+ZZK9KYzFxAqDaVaNvTwLFv3rJRGsXOFWg4+Uw==} + turbo-darwin-arm64@2.6.3: + resolution: {integrity: sha512-MwVt7rBKiOK7zdYerenfCRTypefw4kZCue35IJga9CH1+S50+KTiCkT6LBqo0hHeoH2iKuI0ldTF2a0aB72z3w==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.7.6: - resolution: {integrity: sha512-vjoU8zIfNgvJR3cMitgw7inEoi6bmuVuFawDl5yKtxjAEhDktFdRBpGS3WojD4l3BklBbIK689ssXcGf21LxRA==} + turbo-linux-64@2.6.3: + resolution: {integrity: sha512-cqpcw+dXxbnPtNnzeeSyWprjmuFVpHJqKcs7Jym5oXlu/ZcovEASUIUZVN3OGEM6Y/OTyyw0z09tOHNt5yBAVg==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.7.6: - resolution: {integrity: sha512-TcMpBvTqZf+1DptrVYLbZls7WY1UVNDTGaf0bo7/GCgWYv5eZHCVo4Td7kCJeDU4glbXg67REX0md0S0V6ghMg==} + turbo-linux-arm64@2.6.3: + resolution: {integrity: sha512-MterpZQmjXyr4uM7zOgFSFL3oRdNKeflY7nsjxJb2TklsYqiu3Z9pQ4zRVFFH8n0mLGna7MbQMZuKoWqqHb45w==} cpu: [arm64] os: [linux] - turbo-windows-64@2.7.6: - resolution: {integrity: sha512-1/MhkYldiihjneY8QnnDMbAkHXn/udTWSVYS94EMlkE9AShozsLTTOT1gDOpX06EfEW5njP09suhMvxbvwuwpQ==} + turbo-windows-64@2.6.3: + resolution: {integrity: sha512-biDU70v9dLwnBdLf+daoDlNJVvqOOP8YEjqNipBHzgclbQlXbsi6Gqqelp5er81Qo3BiRgmTNx79oaZQTPb07Q==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.7.6: - resolution: {integrity: sha512-0wDVnUJLFAWm4ZzOQFDkbyyUqaszorTGf3Rdc22IRIyJTTLd6ajqdb+cWD89UZ1RKr953+PZR1gqgWQY4PDuhA==} + turbo-windows-arm64@2.6.3: + resolution: {integrity: sha512-dDHVKpSeukah3VsI/xMEKeTnV9V9cjlpFSUs4bmsUiLu3Yv2ENlgVEZv65wxbeE0bh0jjpmElDT+P1KaCxArQQ==} cpu: [arm64] os: [win32] - turbo@2.7.6: - resolution: {integrity: sha512-PO9AvJLEsNLO+EYhF4zB+v10hOjsJe5kJW+S6tTbRv+TW7gf1Qer4mfjP9h3/y9h8ZiPvOrenxnEgDtFgaM5zw==} + turbo@2.6.3: + resolution: {integrity: sha512-bf6YKUv11l5Xfcmg76PyWoy/e2vbkkxFNBGJSnfdSXQC33ZiUfutYh6IXidc5MhsnrFkWfdNNLyaRk+kHMLlwA==} hasBin: true - twilio@5.12.0: - resolution: {integrity: sha512-ZAKnDKcWvJSb90xQS13QB5KQOeMJPzsRPHxZqju8i5ALg3D4hNwAF9bpytVTxTJV99BL4Rn6Un+ZtXjGeMpjvQ==} + twilio@5.11.1: + resolution: {integrity: sha512-LQuLrAwWk7dsu7S5JQWzLRe17qdD4/7OJcwZG6kYWMJILtxI7pXDHksu9DcIF/vKpSpL1F0/sA9uSF3xuVizMQ==} engines: {node: '>=14.0'} type-check@0.4.0: @@ -5101,6 +5092,10 @@ packages: resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} engines: {node: '>=10'} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5122,8 +5117,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} @@ -5211,8 +5206,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} which@1.3.1: @@ -5224,9 +5219,6 @@ packages: engines: {node: '>= 8'} hasBin: true - win-guid@0.2.0: - resolution: {integrity: sha512-iekGhWzFQSunvE87ndXxoa6UgyQbkL4MmbYTFhQnk94pVsAW89mWnvW1zI3JMnypUm8jykJaITudspoR8NrRBQ==} - word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -5250,8 +5242,8 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -5328,25 +5320,25 @@ snapshots: - '@simplewebauthn/server' - nodemailer - '@babel/code-frame@7.28.6': + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.6': {} + '@babel/compat-data@7.28.5': {} - '@babel/core@7.28.6': + '@babel/core@7.28.5': dependencies: - '@babel/code-frame': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/helper-compilation-targets': 7.28.6 - '@babel/helper-module-transforms': 7.28.6(@babel/core@7.28.6) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/template': 7.28.6 - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 @@ -5356,25 +5348,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/eslint-parser@7.28.4(@babel/core@7.28.6)(eslint@9.39.2)': + '@babel/eslint-parser@7.28.4(@babel/core@7.28.5)(eslint@9.39.2)': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 eslint: 9.39.2 eslint-visitor-keys: 2.1.0 semver: 6.3.1 - '@babel/generator@7.28.6': + '@babel/generator@7.28.5': dependencies: - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.28.6': + '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.6 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 browserslist: 4.28.1 lru-cache: 5.1.1 @@ -5382,23 +5374,23 @@ snapshots: '@babel/helper-globals@7.28.0': {} - '@babel/helper-module-imports@7.28.6': + '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.6 - '@babel/types': 7.28.6 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.6(@babel/core@7.28.6)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-module-imports': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@babel/traverse': 7.28.6 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@7.27.1': {} '@babel/helper-string-parser@7.27.1': {} @@ -5406,146 +5398,148 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.6': + '@babel/helpers@7.28.4': dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.28.6 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 - '@babel/parser@7.28.6': + '@babel/parser@7.28.5': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.6)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.6)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.6)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.6)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.6)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.6)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.28.6(@babel/core@7.28.6)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.5)': dependencies: - '@babel/core': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.28.5 + '@babel/helper-plugin-utils': 7.27.1 - '@babel/runtime@7.28.6': {} + '@babel/runtime@7.28.4': {} - '@babel/template@7.28.6': + '@babel/template@7.27.2': dependencies: - '@babel/code-frame': 7.28.6 - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/traverse@7.28.6': + '@babel/traverse@7.28.5': dependencies: - '@babel/code-frame': 7.28.6 - '@babel/generator': 7.28.6 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.6 - '@babel/template': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/types': 7.28.5 debug: 4.4.3 transitivePeerDependencies: - supports-color - '@babel/types@7.28.6': + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 '@bcoe/v8-coverage@0.2.3': {} - '@borewit/text-codec@0.2.1': {} + '@borewit/text-codec@0.1.1': {} - '@cacheable/memory@2.0.7': + '@borewit/text-codec@0.2.0': {} + + '@cacheable/memory@2.0.6': dependencies: - '@cacheable/utils': 2.3.3 - '@keyv/bigmap': 1.3.1(keyv@5.6.0) - hookified: 1.15.0 - keyv: 5.6.0 + '@cacheable/utils': 2.3.2 + '@keyv/bigmap': 1.3.0(keyv@5.5.5) + hookified: 1.14.0 + keyv: 5.5.5 '@cacheable/node-cache@1.7.6': dependencies: - cacheable: 2.3.2 - hookified: 1.15.0 - keyv: 5.6.0 + cacheable: 2.3.1 + hookified: 1.14.0 + keyv: 5.5.5 - '@cacheable/utils@2.3.3': + '@cacheable/utils@2.3.2': dependencies: - hashery: 1.4.0 - keyv: 5.6.0 + hashery: 1.3.0 + keyv: 5.5.5 '@chatscope/chat-ui-kit-react@2.1.1(prop-types@15.8.1)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: @@ -5565,13 +5559,13 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@emnapi/core@1.8.1': + '@emnapi/core@1.7.1': dependencies: '@emnapi/wasi-threads': 1.1.0 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@1.7.1': dependencies: tslib: 2.8.1 optional: true @@ -5583,8 +5577,8 @@ snapshots: '@emotion/babel-plugin@11.13.5': dependencies: - '@babel/helper-module-imports': 7.28.6 - '@babel/runtime': 7.28.6 + '@babel/helper-module-imports': 7.27.1 + '@babel/runtime': 7.28.4 '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/serialize': 1.3.3 @@ -5615,7 +5609,7 @@ snapshots: '@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 @@ -5648,7 +5642,7 @@ snapshots: '@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@emotion/babel-plugin': 11.13.5 '@emotion/is-prop-valid': 1.4.0 '@emotion/react': 11.14.0(@types/react@19.2.2)(react@19.2.0) @@ -5749,7 +5743,7 @@ snapshots: '@esbuild/win32-x64@0.27.2': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@9.39.2)': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2)': dependencies: eslint: 9.39.2 eslint-visitor-keys: 3.4.3 @@ -6102,7 +6096,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.8.1 + '@emnapi/runtime': 1.7.1 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -6114,14 +6108,14 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/external-editor@1.0.3(@types/node@24.10.9)': + '@inquirer/external-editor@1.0.3(@types/node@24.10.4)': dependencies: chardet: 2.1.1 - iconv-lite: 0.7.2 + iconv-lite: 0.7.1 optionalDependencies: - '@types/node': 24.10.9 + '@types/node': 24.10.4 - '@ioredis/commands@1.5.0': {} + '@ioredis/commands@1.4.0': {} '@isaacs/balanced-match@4.0.1': {} @@ -6151,13 +6145,13 @@ snapshots: '@jest/console@30.2.0': dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 chalk: 4.1.2 jest-message-util: 30.2.0 jest-util: 30.2.0 slash: 3.0.0 - '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3))': + '@jest/core@30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3))': dependencies: '@jest/console': 30.2.0 '@jest/pattern': 30.0.1 @@ -6165,14 +6159,14 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.3.1 exit-x: 0.2.2 graceful-fs: 4.2.11 jest-changed-files: 30.2.0 - jest-config: 30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) jest-haste-map: 30.2.0 jest-message-util: 30.2.0 jest-regex-util: 30.0.1 @@ -6199,7 +6193,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 jest-mock: 30.2.0 '@jest/expect-utils@30.2.0': @@ -6217,7 +6211,7 @@ snapshots: dependencies: '@jest/types': 30.2.0 '@sinonjs/fake-timers': 13.0.5 - '@types/node': 24.10.9 + '@types/node': 24.10.4 jest-message-util: 30.2.0 jest-mock: 30.2.0 jest-util: 30.2.0 @@ -6235,7 +6229,7 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 24.10.9 + '@types/node': 24.10.4 jest-regex-util: 30.0.1 '@jest/reporters@30.2.0': @@ -6246,7 +6240,7 @@ snapshots: '@jest/transform': 30.2.0 '@jest/types': 30.2.0 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 24.10.9 + '@types/node': 24.10.4 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit-x: 0.2.2 @@ -6268,7 +6262,7 @@ snapshots: '@jest/schemas@30.0.5': dependencies: - '@sinclair/typebox': 0.34.48 + '@sinclair/typebox': 0.34.41 '@jest/snapshot-utils@30.2.0': dependencies: @@ -6299,7 +6293,7 @@ snapshots: '@jest/transform@30.2.0': dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/types': 30.2.0 '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 7.0.1 @@ -6323,7 +6317,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 24.10.9 + '@types/node': 24.10.4 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -6351,11 +6345,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@keyv/bigmap@1.3.1(keyv@5.6.0)': + '@keyv/bigmap@1.3.0(keyv@5.5.5)': dependencies: - hashery: 1.4.0 - hookified: 1.15.0 - keyv: 5.6.0 + hashery: 1.3.0 + hookified: 1.14.0 + keyv: 5.5.5 '@keyv/serialize@1.1.1': {} @@ -6365,7 +6359,7 @@ snapshots: '@mui/icons-material@6.5.0(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 optionalDependencies: @@ -6373,7 +6367,7 @@ snapshots: '@mui/material-nextjs@6.5.0(@emotion/cache@11.14.0)(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/server@11.11.0)(@types/react@19.2.2)(next@15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@emotion/react': 11.14.0(@types/react@19.2.2)(react@19.2.0) next: 15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) react: 19.2.0 @@ -6384,7 +6378,7 @@ snapshots: '@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/core-downloads-tracker': 6.5.0 '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) '@mui/types': 7.2.24(@types/react@19.2.2) @@ -6396,7 +6390,7 @@ snapshots: prop-types: 15.8.1 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) - react-is: 19.2.4 + react-is: 19.2.3 react-transition-group: 4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0) optionalDependencies: '@emotion/react': 11.14.0(@types/react@19.2.2)(react@19.2.0) @@ -6405,7 +6399,7 @@ snapshots: '@mui/private-theming@6.4.9(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/utils': 6.4.9(@types/react@19.2.2)(react@19.2.0) prop-types: 15.8.1 react: 19.2.0 @@ -6414,7 +6408,7 @@ snapshots: '@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@emotion/cache': 11.14.0 '@emotion/serialize': 1.3.3 '@emotion/sheet': 1.4.0 @@ -6427,7 +6421,7 @@ snapshots: '@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/private-theming': 6.4.9(@types/react@19.2.2)(react@19.2.0) '@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(react@19.2.0) '@mui/types': 7.2.24(@types/react@19.2.2) @@ -6445,42 +6439,42 @@ snapshots: optionalDependencies: '@types/react': 19.2.2 - '@mui/types@7.4.10(@types/react@19.2.2)': + '@mui/types@7.4.9(@types/react@19.2.2)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 optionalDependencies: '@types/react': 19.2.2 '@mui/utils@6.4.9(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/types': 7.2.24(@types/react@19.2.2) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 19.2.0 - react-is: 19.2.4 + react-is: 19.2.3 optionalDependencies: '@types/react': 19.2.2 - '@mui/utils@7.3.7(@types/react@19.2.2)(react@19.2.0)': + '@mui/utils@7.3.6(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 - '@mui/types': 7.4.10(@types/react@19.2.2) + '@babel/runtime': 7.28.4 + '@mui/types': 7.4.9(@types/react@19.2.2) '@types/prop-types': 15.7.15 clsx: 2.1.1 prop-types: 15.8.1 react: 19.2.0 - react-is: 19.2.4 + react-is: 19.2.3 optionalDependencies: '@types/react': 19.2.2 '@mui/x-data-grid-pro@7.29.12(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) - '@mui/utils': 7.3.7(@types/react@19.2.2)(react@19.2.0) + '@mui/utils': 7.3.6(@types/react@19.2.2)(react@19.2.0) '@mui/x-data-grid': 7.29.12(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mui/x-internals': 7.29.0(@types/react@19.2.2)(react@19.2.0) '@mui/x-license': 7.29.1(@types/react@19.2.2)(react@19.2.0) @@ -6498,10 +6492,10 @@ snapshots: '@mui/x-data-grid@7.29.12(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) - '@mui/utils': 7.3.7(@types/react@19.2.2)(react@19.2.0) + '@mui/utils': 7.3.6(@types/react@19.2.2)(react@19.2.0) '@mui/x-internals': 7.29.0(@types/react@19.2.2)(react@19.2.0) clsx: 2.1.1 prop-types: 15.8.1 @@ -6517,10 +6511,10 @@ snapshots: '@mui/x-date-pickers-pro@7.29.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(date-fns@4.1.0)(dayjs@1.11.19)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) - '@mui/utils': 7.3.7(@types/react@19.2.2)(react@19.2.0) + '@mui/utils': 7.3.6(@types/react@19.2.2)(react@19.2.0) '@mui/x-date-pickers': 7.29.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(date-fns@4.1.0)(dayjs@1.11.19)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mui/x-internals': 7.29.0(@types/react@19.2.2)(react@19.2.0) '@mui/x-license': 7.29.1(@types/react@19.2.2)(react@19.2.0) @@ -6539,10 +6533,10 @@ snapshots: '@mui/x-date-pickers@7.29.4(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@mui/material@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(date-fns@4.1.0)(dayjs@1.11.19)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@mui/material': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) '@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0))(@types/react@19.2.2)(react@19.2.0) - '@mui/utils': 7.3.7(@types/react@19.2.2)(react@19.2.0) + '@mui/utils': 7.3.6(@types/react@19.2.2)(react@19.2.0) '@mui/x-internals': 7.29.0(@types/react@19.2.2)(react@19.2.0) '@types/react-transition-group': 4.4.12(@types/react@19.2.2) clsx: 2.1.1 @@ -6560,16 +6554,16 @@ snapshots: '@mui/x-internals@7.29.0(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 - '@mui/utils': 7.3.7(@types/react@19.2.2)(react@19.2.0) + '@babel/runtime': 7.28.4 + '@mui/utils': 7.3.6(@types/react@19.2.2)(react@19.2.0) react: 19.2.0 transitivePeerDependencies: - '@types/react' '@mui/x-license@7.29.1(@types/react@19.2.2)(react@19.2.0)': dependencies: - '@babel/runtime': 7.28.6 - '@mui/utils': 7.3.7(@types/react@19.2.2)(react@19.2.0) + '@babel/runtime': 7.28.4 + '@mui/utils': 7.3.6(@types/react@19.2.2)(react@19.2.0) '@mui/x-internals': 7.29.0(@types/react@19.2.2)(react@19.2.0) react: 19.2.0 transitivePeerDependencies: @@ -6577,20 +6571,20 @@ snapshots: '@napi-rs/wasm-runtime@0.2.12': dependencies: - '@emnapi/core': 1.8.1 - '@emnapi/runtime': 1.8.1 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true - '@nestjs/axios@4.0.1(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': + '@nestjs/axios@4.0.1(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2) axios: 1.13.2 rxjs: 7.8.2 - '@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - file-type: 21.3.0 + file-type: 21.1.0 iterare: 1.2.1 load-esm: 1.0.3 reflect-metadata: 0.2.2 @@ -6600,9 +6594,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/core@11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)': + '@nestjs/core@11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2)': dependencies: - '@nestjs/common': 11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxt/opencollective': 0.4.1 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -6654,11 +6648,11 @@ snapshots: transitivePeerDependencies: - encoding - '@openapitools/openapi-generator-cli@2.28.0(@types/node@24.10.9)': + '@openapitools/openapi-generator-cli@2.25.2(@types/node@24.10.4)': dependencies: - '@nestjs/axios': 4.0.1(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) - '@nestjs/common': 11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/core': 11.1.12(@nestjs/common@11.1.12(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/axios': 4.0.1(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(axios@1.13.2)(rxjs@7.8.2) + '@nestjs/common': 11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 11.1.9(@nestjs/common@11.1.9(reflect-metadata@0.2.2)(rxjs@7.8.2))(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nuxtjs/opencollective': 0.3.2 axios: 1.13.2 chalk: 4.1.2 @@ -6666,9 +6660,9 @@ snapshots: compare-versions: 6.1.1 concurrently: 9.2.1 console.table: 0.10.0 - fs-extra: 11.3.3 + fs-extra: 11.3.2 glob: 13.0.0 - inquirer: 8.2.7(@types/node@24.10.9) + inquirer: 8.2.7(@types/node@24.10.4) proxy-agent: 6.5.0 reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -6718,86 +6712,77 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@rollup/rollup-android-arm-eabi@4.57.0': + '@rollup/rollup-android-arm-eabi@4.53.5': optional: true - '@rollup/rollup-android-arm64@4.57.0': + '@rollup/rollup-android-arm64@4.53.5': optional: true - '@rollup/rollup-darwin-arm64@4.57.0': + '@rollup/rollup-darwin-arm64@4.53.5': optional: true - '@rollup/rollup-darwin-x64@4.57.0': + '@rollup/rollup-darwin-x64@4.53.5': optional: true - '@rollup/rollup-freebsd-arm64@4.57.0': + '@rollup/rollup-freebsd-arm64@4.53.5': optional: true - '@rollup/rollup-freebsd-x64@4.57.0': + '@rollup/rollup-freebsd-x64@4.53.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.57.0': + '@rollup/rollup-linux-arm-gnueabihf@4.53.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.57.0': + '@rollup/rollup-linux-arm-musleabihf@4.53.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.57.0': + '@rollup/rollup-linux-arm64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.57.0': + '@rollup/rollup-linux-arm64-musl@4.53.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.57.0': + '@rollup/rollup-linux-loong64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-loong64-musl@4.57.0': + '@rollup/rollup-linux-ppc64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.57.0': + '@rollup/rollup-linux-riscv64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-ppc64-musl@4.57.0': + '@rollup/rollup-linux-riscv64-musl@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.57.0': + '@rollup/rollup-linux-s390x-gnu@4.53.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.57.0': + '@rollup/rollup-linux-x64-gnu@4.53.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.57.0': + '@rollup/rollup-linux-x64-musl@4.53.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.57.0': + '@rollup/rollup-openharmony-arm64@4.53.5': optional: true - '@rollup/rollup-linux-x64-musl@4.57.0': + '@rollup/rollup-win32-arm64-msvc@4.53.5': optional: true - '@rollup/rollup-openbsd-x64@4.57.0': + '@rollup/rollup-win32-ia32-msvc@4.53.5': optional: true - '@rollup/rollup-openharmony-arm64@4.57.0': + '@rollup/rollup-win32-x64-gnu@4.53.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.57.0': - optional: true - - '@rollup/rollup-win32-ia32-msvc@4.57.0': - optional: true - - '@rollup/rollup-win32-x64-gnu@4.57.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.57.0': + '@rollup/rollup-win32-x64-msvc@4.53.5': optional: true '@rtsao/scc@1.1.0': {} '@rushstack/eslint-patch@1.15.0': {} - '@sinclair/typebox@0.34.48': {} + '@sinclair/typebox@0.34.41': {} '@sinonjs/commons@3.0.1': dependencies: @@ -6811,10 +6796,18 @@ snapshots: dependencies: tslib: 2.8.1 + '@tokenizer/inflate@0.3.1': + dependencies: + debug: 4.4.3 + fflate: 0.8.2 + token-types: 6.1.1 + transitivePeerDependencies: + - supports-color + '@tokenizer/inflate@0.4.1': dependencies: debug: 4.4.3 - token-types: 6.1.2 + token-types: 6.1.1 transitivePeerDependencies: - supports-color @@ -6837,24 +6830,24 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.6 - '@babel/types': 7.28.6 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.6 + '@babel/types': 7.28.5 '@types/debug@4.1.12': dependencies: @@ -6864,7 +6857,7 @@ snapshots: '@types/fluent-ffmpeg@2.1.28': dependencies: - '@types/node': 24.10.9 + '@types/node': 24.10.4 '@types/format-util@1.0.4': {} @@ -6875,7 +6868,7 @@ snapshots: '@types/interpret@1.1.4': dependencies: - '@types/node': 24.10.9 + '@types/node': 24.10.4 '@types/istanbul-lib-coverage@2.0.6': {} @@ -6906,11 +6899,11 @@ snapshots: '@types/node@10.17.60': {} - '@types/node@22.19.7': + '@types/node@22.19.3': dependencies: undici-types: 6.21.0 - '@types/node@24.10.9': + '@types/node@24.10.4': dependencies: undici-types: 7.16.0 @@ -6918,8 +6911,8 @@ snapshots: '@types/pg@8.16.0': dependencies: - '@types/node': 24.10.9 - pg-protocol: 1.11.0 + '@types/node': 24.10.4 + pg-protocol: 1.10.3 pg-types: 2.2.0 '@types/prop-types@15.7.15': {} @@ -6946,95 +6939,95 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/type-utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 eslint: 9.39.2 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3 eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.54.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.54.0': + '@typescript-eslint/scope-manager@8.50.0': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 - '@typescript-eslint/tsconfig-utils@8.54.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.50.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.54.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) debug: 4.4.3 eslint: 9.39.2 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.54.0': {} + '@typescript-eslint/types@8.50.0': {} - '@typescript-eslint/typescript-estree@8.54.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.50.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.54.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.54.0(typescript@5.9.3) - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/visitor-keys': 8.54.0 + '@typescript-eslint/project-service': 8.50.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.0(typescript@5.9.3) + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/visitor-keys': 8.50.0 debug: 4.4.3 minimatch: 9.0.5 semver: 7.7.3 tinyglobby: 0.2.15 - ts-api-utils: 2.4.0(typescript@5.9.3) + ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.54.0(eslint@9.39.2)(typescript@5.9.3)': + '@typescript-eslint/utils@8.50.0(eslint@9.39.2)(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) - '@typescript-eslint/scope-manager': 8.54.0 - '@typescript-eslint/types': 8.54.0 - '@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) + '@typescript-eslint/scope-manager': 8.50.0 + '@typescript-eslint/types': 8.50.0 + '@typescript-eslint/typescript-estree': 8.50.0(typescript@5.9.3) eslint: 9.39.2 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.54.0': + '@typescript-eslint/visitor-keys@8.50.0': dependencies: - '@typescript-eslint/types': 8.54.0 + '@typescript-eslint/types': 8.50.0 eslint-visitor-keys: 4.2.1 '@ungap/structured-clone@1.3.0': {} @@ -7103,13 +7096,13 @@ snapshots: '@cacheable/node-cache': 1.7.6 '@hapi/boom': 9.1.4 async-mutex: 0.5.0 - axios: 1.13.4 + axios: 1.13.2 libsignal: '@whiskeysockets/libsignal-node@https://codeload.github.com/whiskeysockets/libsignal-node/tar.gz/1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67' - music-metadata: 11.11.0 + music-metadata: 11.10.3 pino: 9.14.0 protobufjs: 7.5.4 sharp: 0.34.5 - ws: 8.19.0 + ws: 8.18.3 optionalDependencies: link-preview-js: 3.2.0 transitivePeerDependencies: @@ -7259,21 +7252,13 @@ snapshots: transitivePeerDependencies: - debug - axios@1.13.4: + babel-jest@30.2.0(@babel/core@7.28.5): dependencies: - follow-redirects: 1.15.11 - form-data: 4.0.5 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - - babel-jest@30.2.0(@babel/core@7.28.6): - dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/transform': 30.2.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 7.0.1 - babel-preset-jest: 30.2.0(@babel/core@7.28.6) + babel-preset-jest: 30.2.0(@babel/core@7.28.5) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -7282,7 +7267,7 @@ snapshots: babel-plugin-istanbul@7.0.1: dependencies: - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.27.1 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 istanbul-lib-instrument: 6.0.3 @@ -7296,42 +7281,42 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 cosmiconfig: 7.1.0 resolve: 1.22.11 - babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.6): + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.6) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.6) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.6) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.6) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.6) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.6) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.6) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.6) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.6) + '@babel/core': 7.28.5 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.5) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.5) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.5) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.5) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.5) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.5) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.5) - babel-preset-jest@30.2.0(@babel/core@7.28.6): + babel-preset-jest@30.2.0(@babel/core@7.28.5): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 babel-plugin-jest-hoist: 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.6) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) balanced-match@1.0.2: {} base64-js@1.5.1: {} - baseline-browser-mapping@2.9.18: {} + baseline-browser-mapping@2.9.10: {} - basic-ftp@5.1.0: {} + basic-ftp@5.0.5: {} bl@4.1.0: dependencies: @@ -7356,9 +7341,9 @@ snapshots: browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.9.18 - caniuse-lite: 1.0.30001766 - electron-to-chromium: 1.5.279 + baseline-browser-mapping: 2.9.10 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) @@ -7386,13 +7371,13 @@ snapshots: cac@6.7.14: {} - cacheable@2.3.2: + cacheable@2.3.1: dependencies: - '@cacheable/memory': 2.0.7 - '@cacheable/utils': 2.3.3 - hookified: 1.15.0 - keyv: 5.6.0 - qified: 0.6.0 + '@cacheable/memory': 2.0.6 + '@cacheable/utils': 2.3.2 + hookified: 1.14.0 + keyv: 5.5.5 + qified: 0.5.3 call-bind-apply-helpers@1.0.2: dependencies: @@ -7417,7 +7402,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001766: {} + caniuse-lite@1.0.30001761: {} chalk@4.1.2: dependencies: @@ -7456,7 +7441,7 @@ snapshots: ci-info@4.3.1: {} - cjs-module-lexer@2.2.0: {} + cjs-module-lexer@2.1.1: {} classnames@2.5.1: {} @@ -7541,7 +7526,7 @@ snapshots: cookie@1.1.1: {} - core-js-compat@3.48.0: + core-js-compat@3.47.0: dependencies: browserslist: 4.28.1 @@ -7658,7 +7643,7 @@ snapshots: detect-newline@3.1.0: {} - diff@4.0.4: {} + diff@4.0.2: {} doctrine@2.1.0: dependencies: @@ -7666,7 +7651,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 csstype: 3.2.3 dom-serializer@2.0.0: @@ -7733,7 +7718,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - electron-to-chromium@1.5.279: {} + electron-to-chromium@1.5.267: {} emittery@0.13.1: {} @@ -7808,7 +7793,7 @@ snapshots: typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 + which-typed-array: 1.1.19 es-define-property@1.0.1: {} @@ -7902,11 +7887,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 eslint-import-resolver-node: 0.3.9 transitivePeerDependencies: @@ -7918,7 +7903,7 @@ snapshots: eslint: 9.39.2 ignore: 5.3.2 - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -7929,7 +7914,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.39.2 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.2) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -7941,39 +7926,39 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.0(eslint@9.39.2)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jest@29.12.1(@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(jest@30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)))(typescript@5.9.3): + eslint-plugin-jest@29.5.0(@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(jest@30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)))(typescript@5.9.3): dependencies: - '@typescript-eslint/utils': 8.54.0(eslint@9.39.2)(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.0(eslint@9.39.2)(typescript@5.9.3) eslint: 9.39.2 optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) - jest: 30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + '@typescript-eslint/eslint-plugin': 8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.2)(typescript@5.9.3))(eslint@9.39.2)(typescript@5.9.3) + jest: 30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) transitivePeerDependencies: - supports-color - typescript eslint-plugin-promise@7.2.1(eslint@9.39.2): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) eslint: 9.39.2 eslint-plugin-unicorn@61.0.2(eslint@9.39.2): dependencies: '@babel/helper-validator-identifier': 7.28.5 - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@eslint/plugin-kit': 0.3.5 change-case: 5.4.4 ci-info: 4.3.1 clean-regexp: 1.0.0 - core-js-compat: 3.48.0 + core-js-compat: 3.47.0 eslint: 9.39.2 - esquery: 1.7.0 + esquery: 1.6.0 find-up-simple: 1.0.1 globals: 16.5.0 indent-string: 5.0.0 @@ -8003,7 +7988,7 @@ snapshots: eslint@9.39.2: dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 @@ -8023,7 +8008,7 @@ snapshots: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 - esquery: 1.7.0 + esquery: 1.6.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 @@ -8048,7 +8033,7 @@ snapshots: esprima@4.0.1: {} - esquery@1.7.0: + esquery@1.6.0: dependencies: estraverse: 5.3.0 @@ -8103,6 +8088,8 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.8.2: {} + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -8111,11 +8098,20 @@ snapshots: dependencies: flat-cache: 4.0.1 - file-type@21.3.0: + file-type@21.1.0: + dependencies: + '@tokenizer/inflate': 0.3.1 + strtok3: 10.3.4 + token-types: 6.1.1 + uint8array-extras: 1.5.0 + transitivePeerDependencies: + - supports-color + + file-type@21.1.1: dependencies: '@tokenizer/inflate': 0.4.1 strtok3: 10.3.4 - token-types: 6.1.2 + token-types: 6.1.1 uint8array-extras: 1.5.0 transitivePeerDependencies: - supports-color @@ -8142,7 +8138,7 @@ snapshots: dependencies: magic-string: 0.30.21 mlly: 1.8.0 - rollup: 4.57.0 + rollup: 4.53.5 flat-cache@4.0.1: dependencies: @@ -8175,7 +8171,7 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - fs-extra@11.3.3: + fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 @@ -8239,7 +8235,7 @@ snapshots: get-uri@6.0.5: dependencies: - basic-ftp: 5.1.0 + basic-ftp: 5.0.5 data-uri-to-buffer: 6.0.2 debug: 4.4.3 transitivePeerDependencies: @@ -8298,7 +8294,7 @@ snapshots: graphile-config@0.0.1-beta.18: dependencies: '@types/interpret': 1.1.4 - '@types/node': 22.19.7 + '@types/node': 22.19.3 '@types/semver': 7.7.1 chalk: 4.1.2 debug: 4.4.3 @@ -8317,7 +8313,7 @@ snapshots: cosmiconfig: 8.3.6(typescript@5.9.3) graphile-config: 0.0.1-beta.18 json5: 2.2.3 - pg: 8.17.2 + pg: 8.16.3 tslib: 2.8.1 yargs: 17.7.2 transitivePeerDependencies: @@ -8357,9 +8353,9 @@ snapshots: dependencies: has-symbols: 1.1.0 - hashery@1.4.0: + hashery@1.3.0: dependencies: - hookified: 1.15.0 + hookified: 1.14.0 hasown@2.0.2: dependencies: @@ -8371,7 +8367,7 @@ snapshots: dependencies: react-is: 16.13.1 - hookified@1.15.0: {} + hookified@1.14.0: {} html-escaper@2.0.2: {} @@ -8413,7 +8409,7 @@ snapshots: human-signals@2.1.0: {} - iconv-lite@0.7.2: + iconv-lite@0.7.1: dependencies: safer-buffer: 2.1.2 @@ -8444,15 +8440,15 @@ snapshots: inherits@2.0.4: {} - inquirer@8.2.7(@types/node@24.10.9): + inquirer@8.2.7(@types/node@24.10.4): dependencies: - '@inquirer/external-editor': 1.0.3(@types/node@24.10.9) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.4) ansi-escapes: 4.3.2 chalk: 4.1.2 cli-cursor: 3.1.0 cli-width: 3.0.0 figures: 3.2.0 - lodash: 4.17.23 + lodash: 4.17.21 mute-stream: 0.0.8 ora: 5.4.1 run-async: 2.4.1 @@ -8472,9 +8468,9 @@ snapshots: interpret@3.1.1: {} - ioredis@5.9.2: + ioredis@5.8.2: dependencies: - '@ioredis/commands': 1.5.0 + '@ioredis/commands': 1.4.0 cluster-key-slot: 1.1.2 debug: 4.4.3 denque: 2.1.0 @@ -8597,7 +8593,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.19 is-unicode-supported@0.1.0: {} @@ -8624,8 +8620,8 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.28.6 - '@babel/parser': 7.28.6 + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.3 @@ -8675,7 +8671,7 @@ snapshots: '@jest/expect': 30.2.0 '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.1(babel-plugin-macros@3.1.0) @@ -8695,15 +8691,15 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)): + jest-cli@30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + jest-config: 30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) jest-util: 30.2.0 jest-validate: 30.2.0 yargs: 17.7.2 @@ -8714,14 +8710,14 @@ snapshots: - supports-color - ts-node - jest-config@30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)): + jest-config@30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)): dependencies: - '@babel/core': 7.28.6 + '@babel/core': 7.28.5 '@jest/get-type': 30.1.0 '@jest/pattern': 30.0.1 '@jest/test-sequencer': 30.2.0 '@jest/types': 30.2.0 - babel-jest: 30.2.0(@babel/core@7.28.6) + babel-jest: 30.2.0(@babel/core@7.28.5) chalk: 4.1.2 ci-info: 4.3.1 deepmerge: 4.3.1 @@ -8741,8 +8737,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 24.10.9 - ts-node: 10.9.2(@types/node@24.10.9)(typescript@5.9.3) + '@types/node': 24.10.4 + ts-node: 10.9.2(@types/node@24.10.4)(typescript@5.9.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -8771,7 +8767,7 @@ snapshots: '@jest/environment': 30.2.0 '@jest/fake-timers': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 jest-mock: 30.2.0 jest-util: 30.2.0 jest-validate: 30.2.0 @@ -8779,7 +8775,7 @@ snapshots: jest-haste-map@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -8812,7 +8808,7 @@ snapshots: jest-message-util@30.2.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.27.1 '@jest/types': 30.2.0 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -8825,7 +8821,7 @@ snapshots: jest-mock@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 jest-util: 30.2.0 jest-pnp-resolver@1.2.3(jest-resolve@30.2.0): @@ -8859,7 +8855,7 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -8888,9 +8884,9 @@ snapshots: '@jest/test-result': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 chalk: 4.1.2 - cjs-module-lexer: 2.2.0 + cjs-module-lexer: 2.1.1 collect-v8-coverage: 1.0.3 glob: 10.5.0 graceful-fs: 4.2.11 @@ -8908,17 +8904,17 @@ snapshots: jest-snapshot@30.2.0: dependencies: - '@babel/core': 7.28.6 - '@babel/generator': 7.28.6 - '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.28.6) - '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.28.6) - '@babel/types': 7.28.6 + '@babel/core': 7.28.5 + '@babel/generator': 7.28.5 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.5) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) + '@babel/types': 7.28.5 '@jest/expect-utils': 30.2.0 '@jest/get-type': 30.1.0 '@jest/snapshot-utils': 30.2.0 '@jest/transform': 30.2.0 '@jest/types': 30.2.0 - babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.6) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.5) chalk: 4.1.2 expect: 30.2.0 graceful-fs: 4.2.11 @@ -8928,14 +8924,14 @@ snapshots: jest-util: 30.2.0 pretty-format: 30.2.0 semver: 7.7.3 - synckit: 0.11.12 + synckit: 0.11.11 transitivePeerDependencies: - supports-color jest-util@30.2.0: dependencies: '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 chalk: 4.1.2 ci-info: 4.3.1 graceful-fs: 4.2.11 @@ -8954,7 +8950,7 @@ snapshots: dependencies: '@jest/test-result': 30.2.0 '@jest/types': 30.2.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -8963,18 +8959,18 @@ snapshots: jest-worker@30.2.0: dependencies: - '@types/node': 24.10.9 + '@types/node': 24.10.4 '@ungap/structured-clone': 1.3.0 jest-util: 30.2.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)): + jest@30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)): dependencies: - '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + '@jest/core': 30.2.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) '@jest/types': 30.2.0 import-local: 3.2.0 - jest-cli: 30.2.0(@types/node@24.10.9)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3)) + jest-cli: 30.2.0(@types/node@24.10.4)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -9051,7 +9047,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - keyv@5.6.0: + keyv@5.5.5: dependencies: '@keyv/serialize': 1.1.1 @@ -9105,7 +9101,7 @@ snapshots: lodash.once@4.1.1: {} - lodash@4.17.23: {} + lodash@4.17.21: {} log-symbols@4.1.0: dependencies: @@ -9122,7 +9118,7 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.2.5: {} + lru-cache@11.2.4: {} lru-cache@5.1.1: dependencies: @@ -9192,7 +9188,7 @@ snapshots: acorn: 8.15.0 pathe: 2.0.3 pkg-types: 1.3.1 - ufo: 1.6.3 + ufo: 1.6.1 ms@2.1.3: {} @@ -9212,18 +9208,17 @@ snapshots: duplexer2: 0.1.4 object-assign: 4.1.1 - music-metadata@11.11.0: + music-metadata@11.10.3: dependencies: - '@borewit/text-codec': 0.2.1 + '@borewit/text-codec': 0.2.0 '@tokenizer/token': 0.3.0 content-type: 1.0.5 debug: 4.4.3 - file-type: 21.3.0 + file-type: 21.1.1 media-typer: 1.1.0 strtok3: 10.3.4 - token-types: 6.1.2 + token-types: 6.1.1 uint8array-extras: 1.5.0 - win-guid: 0.2.0 transitivePeerDependencies: - supports-color @@ -9245,15 +9240,15 @@ snapshots: next-auth@4.24.13(next@15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 '@panva/hkdf': 1.2.1 cookie: 0.7.2 jose: 4.15.9 next: 15.5.9(react-dom@19.2.0(react@19.2.0))(react@19.2.0) oauth: 0.9.15 openid-client: 5.7.1 - preact: 10.28.2 - preact-render-to-string: 5.2.6(preact@10.28.2) + preact: 10.28.0 + preact-render-to-string: 5.2.6(preact@10.28.0) react: 19.2.0 react-dom: 19.2.0(react@19.2.0) uuid: 8.3.2 @@ -9262,7 +9257,7 @@ snapshots: dependencies: '@next/env': 15.5.9 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001766 + caniuse-lite: 1.0.30001761 postcss: 8.4.31 react: 19.2.0 react-dom: 19.2.0(react@19.2.0) @@ -9445,7 +9440,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.28.6 + '@babel/code-frame': 7.27.1 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -9474,7 +9469,7 @@ snapshots: path-scurry@2.0.1: dependencies: - lru-cache: 11.2.5 + lru-cache: 11.2.4 minipass: 7.1.2 path-to-regexp@8.3.0: {} @@ -9483,18 +9478,18 @@ snapshots: pathe@2.0.3: {} - pg-cloudflare@1.3.0: + pg-cloudflare@1.2.7: optional: true - pg-connection-string@2.10.1: {} + pg-connection-string@2.9.1: {} pg-int8@1.0.1: {} - pg-pool@3.11.0(pg@8.17.2): + pg-pool@3.10.1(pg@8.16.3): dependencies: - pg: 8.17.2 + pg: 8.16.3 - pg-protocol@1.11.0: {} + pg-protocol@1.10.3: {} pg-types@2.2.0: dependencies: @@ -9504,15 +9499,15 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg@8.17.2: + pg@8.16.3: dependencies: - pg-connection-string: 2.10.1 - pg-pool: 3.11.0(pg@8.17.2) - pg-protocol: 1.11.0 + pg-connection-string: 2.9.1 + pg-pool: 3.10.1(pg@8.16.3) + pg-protocol: 1.10.3 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.3.0 + pg-cloudflare: 1.2.7 pgpass@1.0.5: dependencies: @@ -9548,21 +9543,21 @@ snapshots: sonic-boom: 4.2.0 strip-json-comments: 5.0.3 - pino-std-serializers@7.1.0: {} + pino-std-serializers@7.0.0: {} - pino@10.3.0: + pino@10.1.0: dependencies: '@pinojs/redact': 0.4.0 atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 - pino-abstract-transport: 3.0.0 - pino-std-serializers: 7.1.0 + pino-abstract-transport: 2.0.0 + pino-std-serializers: 7.0.0 process-warning: 5.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 safe-stable-stringify: 2.5.0 sonic-boom: 4.2.0 - thread-stream: 4.0.0 + thread-stream: 3.1.0 pino@9.14.0: dependencies: @@ -9570,7 +9565,7 @@ snapshots: atomic-sleep: 1.0.0 on-exit-leak-free: 2.1.2 pino-abstract-transport: 2.0.0 - pino-std-serializers: 7.1.0 + pino-std-serializers: 7.0.0 process-warning: 5.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 @@ -9617,9 +9612,9 @@ snapshots: dependencies: xtend: 4.0.2 - preact-render-to-string@5.2.6(preact@10.28.2): + preact-render-to-string@5.2.6(preact@10.28.0): dependencies: - preact: 10.28.2 + preact: 10.28.0 pretty-format: 3.8.0 preact-render-to-string@6.5.11(preact@10.24.3): @@ -9628,7 +9623,7 @@ snapshots: preact@10.24.3: {} - preact@10.28.2: {} + preact@10.28.0: {} prelude-ls@1.2.1: {} @@ -9678,7 +9673,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 24.10.9 + '@types/node': 24.10.4 long: 5.3.2 proxy-agent@6.5.0: @@ -9707,13 +9702,13 @@ snapshots: pure-rand@7.0.1: {} - qified@0.6.0: + qified@0.5.3: dependencies: - hookified: 1.15.0 + hookified: 1.14.0 qr.js@0.0.0: {} - qs@6.14.1: + qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -9744,7 +9739,7 @@ snapshots: react-is@18.3.1: {} - react-is@19.2.4: {} + react-is@19.2.3: {} react-polyglot@0.7.2(node-polyglot@2.6.0)(react@19.2.0): dependencies: @@ -9761,7 +9756,7 @@ snapshots: react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.28.4 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 @@ -9831,7 +9826,9 @@ snapshots: dependencies: jsesc: 3.0.2 - remeda@2.33.4: {} + remeda@2.32.0: + dependencies: + type-fest: 4.41.0 require-directory@2.1.1: {} @@ -9858,35 +9855,32 @@ snapshots: onetime: 5.1.2 signal-exit: 3.0.7 - rollup@4.57.0: + rollup@4.53.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.57.0 - '@rollup/rollup-android-arm64': 4.57.0 - '@rollup/rollup-darwin-arm64': 4.57.0 - '@rollup/rollup-darwin-x64': 4.57.0 - '@rollup/rollup-freebsd-arm64': 4.57.0 - '@rollup/rollup-freebsd-x64': 4.57.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.57.0 - '@rollup/rollup-linux-arm-musleabihf': 4.57.0 - '@rollup/rollup-linux-arm64-gnu': 4.57.0 - '@rollup/rollup-linux-arm64-musl': 4.57.0 - '@rollup/rollup-linux-loong64-gnu': 4.57.0 - '@rollup/rollup-linux-loong64-musl': 4.57.0 - '@rollup/rollup-linux-ppc64-gnu': 4.57.0 - '@rollup/rollup-linux-ppc64-musl': 4.57.0 - '@rollup/rollup-linux-riscv64-gnu': 4.57.0 - '@rollup/rollup-linux-riscv64-musl': 4.57.0 - '@rollup/rollup-linux-s390x-gnu': 4.57.0 - '@rollup/rollup-linux-x64-gnu': 4.57.0 - '@rollup/rollup-linux-x64-musl': 4.57.0 - '@rollup/rollup-openbsd-x64': 4.57.0 - '@rollup/rollup-openharmony-arm64': 4.57.0 - '@rollup/rollup-win32-arm64-msvc': 4.57.0 - '@rollup/rollup-win32-ia32-msvc': 4.57.0 - '@rollup/rollup-win32-x64-gnu': 4.57.0 - '@rollup/rollup-win32-x64-msvc': 4.57.0 + '@rollup/rollup-android-arm-eabi': 4.53.5 + '@rollup/rollup-android-arm64': 4.53.5 + '@rollup/rollup-darwin-arm64': 4.53.5 + '@rollup/rollup-darwin-x64': 4.53.5 + '@rollup/rollup-freebsd-arm64': 4.53.5 + '@rollup/rollup-freebsd-x64': 4.53.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.5 + '@rollup/rollup-linux-arm-musleabihf': 4.53.5 + '@rollup/rollup-linux-arm64-gnu': 4.53.5 + '@rollup/rollup-linux-arm64-musl': 4.53.5 + '@rollup/rollup-linux-loong64-gnu': 4.53.5 + '@rollup/rollup-linux-ppc64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-gnu': 4.53.5 + '@rollup/rollup-linux-riscv64-musl': 4.53.5 + '@rollup/rollup-linux-s390x-gnu': 4.53.5 + '@rollup/rollup-linux-x64-gnu': 4.53.5 + '@rollup/rollup-linux-x64-musl': 4.53.5 + '@rollup/rollup-openharmony-arm64': 4.53.5 + '@rollup/rollup-win32-arm64-msvc': 4.53.5 + '@rollup/rollup-win32-ia32-msvc': 4.53.5 + '@rollup/rollup-win32-x64-gnu': 4.53.5 + '@rollup/rollup-win32-x64-msvc': 4.53.5 fsevents: 2.3.3 run-async@2.4.1: {} @@ -10175,7 +10169,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - synckit@0.11.12: + synckit@0.11.11: dependencies: '@pkgr/core': 0.2.9 @@ -10197,10 +10191,6 @@ snapshots: dependencies: real-require: 0.2.0 - thread-stream@4.0.0: - dependencies: - real-require: 0.2.0 - through2@0.4.2: dependencies: readable-stream: 1.0.34 @@ -10221,9 +10211,9 @@ snapshots: dependencies: is-number: 7.0.0 - token-types@6.1.2: + token-types@6.1.1: dependencies: - '@borewit/text-codec': 0.2.1 + '@borewit/text-codec': 0.1.1 '@tokenizer/token': 0.3.0 ieee754: 1.2.1 @@ -10231,25 +10221,25 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.4.0(typescript@5.9.3): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: typescript: 5.9.3 ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@24.10.9)(typescript@5.9.3): + ts-node@10.9.2(@types/node@24.10.4)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 24.10.9 + '@types/node': 24.10.4 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.4 + diff: 4.0.2 make-error: 1.3.6 typescript: 5.9.3 v8-compile-cache-lib: 3.0.1 @@ -10277,7 +10267,7 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(postcss@8.4.31)(tsx@4.21.0) resolve-from: 5.0.0 - rollup: 4.57.0 + rollup: 4.53.5 source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 @@ -10299,40 +10289,40 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@2.7.6: + turbo-darwin-64@2.6.3: optional: true - turbo-darwin-arm64@2.7.6: + turbo-darwin-arm64@2.6.3: optional: true - turbo-linux-64@2.7.6: + turbo-linux-64@2.6.3: optional: true - turbo-linux-arm64@2.7.6: + turbo-linux-arm64@2.6.3: optional: true - turbo-windows-64@2.7.6: + turbo-windows-64@2.6.3: optional: true - turbo-windows-arm64@2.7.6: + turbo-windows-arm64@2.6.3: optional: true - turbo@2.7.6: + turbo@2.6.3: optionalDependencies: - turbo-darwin-64: 2.7.6 - turbo-darwin-arm64: 2.7.6 - turbo-linux-64: 2.7.6 - turbo-linux-arm64: 2.7.6 - turbo-windows-64: 2.7.6 - turbo-windows-arm64: 2.7.6 + turbo-darwin-64: 2.6.3 + turbo-darwin-arm64: 2.6.3 + turbo-linux-64: 2.6.3 + turbo-linux-arm64: 2.6.3 + turbo-windows-64: 2.6.3 + turbo-windows-arm64: 2.6.3 - twilio@5.12.0: + twilio@5.11.1: dependencies: - axios: 1.13.4 + axios: 1.13.2 dayjs: 1.11.19 https-proxy-agent: 5.0.1 jsonwebtoken: 9.0.3 - qs: 6.14.1 + qs: 6.14.0 scmp: 2.1.0 xmlbuilder: 13.0.2 transitivePeerDependencies: @@ -10347,6 +10337,8 @@ snapshots: type-fest@0.21.3: {} + type-fest@4.41.0: {} + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -10382,7 +10374,7 @@ snapshots: typescript@5.9.3: {} - ufo@1.6.3: {} + ufo@1.6.1: {} uid@2.0.2: dependencies: @@ -10503,7 +10495,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.20 + which-typed-array: 1.1.19 which-collection@1.0.2: dependencies: @@ -10512,7 +10504,7 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.20: + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 @@ -10530,8 +10522,6 @@ snapshots: dependencies: isexe: 2.0.0 - win-guid@0.2.0: {} - word-wrap@1.2.5: {} wrap-ansi@6.2.0: @@ -10559,7 +10549,7 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 - ws@8.19.0: {} + ws@8.18.3: {} xml@1.0.1: {}