diff --git a/integrations/wechat/definitions/channels.ts b/integrations/wechat/definitions/channels.ts new file mode 100644 index 00000000000..c2f8ae6360d --- /dev/null +++ b/integrations/wechat/definitions/channels.ts @@ -0,0 +1,33 @@ +import { IntegrationDefinitionProps, messages } from '@botpress/sdk' + +const _wechatMessageChannels = { + text: { + ...messages.defaults.text, + schema: messages.defaults.text.schema.extend({ + text: messages.defaults.text.schema.shape.text + .max(4096) + .describe('The text content of the WeChat message (Limit 4096 characters)'), + }), + }, + image: messages.defaults.image, + video: messages.defaults.video, +} + +export const channels = { + channel: { + title: 'Channel', + description: 'WeChat Channel', + messages: _wechatMessageChannels, + message: { + tags: { + id: { title: 'ID', description: 'The message ID' }, + chatId: { title: 'Chat ID', description: 'The message chat ID' }, + }, + }, + conversation: { + tags: { + id: { title: 'ID', description: "The WeChat conversation ID (This is also the Recipient's UserId)" }, + }, + }, + }, +} as const satisfies IntegrationDefinitionProps['channels'] diff --git a/integrations/wechat/definitions/index.ts b/integrations/wechat/definitions/index.ts new file mode 100644 index 00000000000..43a31fb5105 --- /dev/null +++ b/integrations/wechat/definitions/index.ts @@ -0,0 +1,3 @@ +export * from './channels' +export * from './user' +export * from './states' diff --git a/integrations/wechat/definitions/states.ts b/integrations/wechat/definitions/states.ts new file mode 100644 index 00000000000..b4901d5402d --- /dev/null +++ b/integrations/wechat/definitions/states.ts @@ -0,0 +1,22 @@ +import { type IntegrationDefinitionProps, z } from '@botpress/sdk' + +export const states = { + configuration: { + type: 'integration', + schema: z.object({ + auth: z + .object({ + accessToken: z.string().title('Access Token').describe('The access token for the integration'), + expiresAt: z + .number() + .min(0) + .title('Expires At') + .describe('The expiry time of the access token represented as a Unix timestamp (seconds)'), + }) + .nullable() + .default(null) + .title('Auth Parameters') + .describe('The parameters used for accessing the WeChat API'), + }), + }, +} as const satisfies IntegrationDefinitionProps['states'] diff --git a/integrations/wechat/definitions/user.ts b/integrations/wechat/definitions/user.ts new file mode 100644 index 00000000000..c248d31308e --- /dev/null +++ b/integrations/wechat/definitions/user.ts @@ -0,0 +1,7 @@ +import { IntegrationDefinitionProps } from '@botpress/sdk' + +export const user = { + tags: { + id: { title: 'ID', description: 'The ID of the user' }, + }, +} as const satisfies IntegrationDefinitionProps['user'] diff --git a/integrations/wechat/hub.md b/integrations/wechat/hub.md new file mode 100644 index 00000000000..0ebb6b4c65a --- /dev/null +++ b/integrations/wechat/hub.md @@ -0,0 +1,93 @@ +# WeChat Official Account Integration + +Connect your Botpress chatbot to WeChat Official Accounts and engage with your Chinese audience in real-time. + +## Prerequisites + +Before you begin, you need: + +1. **WeChat Official Account** (Service Account or Subscription Account) + - Create one at: https://mp.weixin.qq.com/ +2. **App ID and App Secret** from your WeChat Official Account settings +3. **Server Configuration** enabled in WeChat Admin Panel + +## Configuration + +### 1. Get Your WeChat Credentials + +In your WeChat Official Account admin panel: + +1. Go to **Settings & Development** > **Basic Configuration** +2. Copy your **AppID** and **AppSecret** +3. Generate a **Token** (any random string, you'll use this in both WeChat and Botpress) + +### 2. Install the Integration in Botpress + +1. Install this integration in your Botpress workspace +2. Configure with your credentials: + - **WeChat Token**: The token you generated (used for signature verification) + - **App ID**: Your WeChat Official Account AppID + - **App Secret**: Your WeChat Official Account AppSecret + +### 3. Configure WeChat Webhook + +In your WeChat Official Account admin panel: + +1. Go to **Settings & Development** > **Basic Configuration** +2. Click **Enable** Server Configuration +3. Set the **URL** to https://wechat.botpress.tech/{your Botpress webhook ID}, where the webhook ID is the string provided after installing the integration. +4. Set the **Token** to the same token you used in Botpress configuration +5. Click **Submit** - WeChat will verify your server + +## Supported Features + +### Receiving Messages + +Your bot can receive the following message types from WeChat users: + +- **Text messages** - Plain text messages +- **Image messages** - Photos sent by users (PicUrl provided) +- **Video messages** - Video content +- **Link messages** - Shared links with title, description, and URL + +### Sending Messages + +Your bot can send the following message types to WeChat users: + +- **Text messages** - Up to 4,096 characters +- **Image messages** - Images (automatically uploaded to WeChat) + +## Limitations + +### WeChat Platform Limitations + +- **Official Account Required**: Personal WeChat accounts cannot be used +- **Customer Service API Window**: Can only send messages to users who have messaged you within the last 48 hours +- **Message Length**: Text messages limited to 4,096 characters +- **No Proactive Messaging**: Cannot initiate conversations; users must message first + +## Troubleshooting + +### Webhook Verification Fails + +- Ensure your **Token** matches exactly in both Botpress and WeChat +- Check that your webhook URL is publicly accessible +- Verify the URL ends with your integration webhook path + +### Messages Not Appearing in Botpress + +- Check your WeChat Admin Panel logs for delivery errors +- Verify your **App ID** and **App Secret** are correct +- Ensure Server Configuration is enabled in WeChat + +### Bot Not Responding + +- Check Botpress logs for errors +- Verify the user messaged you within the last 48 hours +- Ensure your bot flow is properly configured + +## Additional Resources + +- [WeChat Official Account Platform Docs](https://developers.weixin.qq.com/doc/offiaccount/en/Getting_Started/Overview.html) +- [WeChat API Reference](https://developers.weixin.qq.com/doc/offiaccount/en/Message_Management/Receiving_standard_messages.html) +- [Botpress Documentation](https://botpress.com/docs) diff --git a/integrations/wechat/icon.svg b/integrations/wechat/icon.svg new file mode 100644 index 00000000000..417a59c997c --- /dev/null +++ b/integrations/wechat/icon.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/integrations/wechat/integration.definition.ts b/integrations/wechat/integration.definition.ts new file mode 100644 index 00000000000..f37faa7be25 --- /dev/null +++ b/integrations/wechat/integration.definition.ts @@ -0,0 +1,24 @@ +import { z, IntegrationDefinition } from '@botpress/sdk' +import { channels, states, user } from 'definitions' + +export default new IntegrationDefinition({ + name: 'wechat', + title: 'WeChat', + description: 'Engage with your WeChat audience in real-time.', + version: '0.1.0', + readme: 'hub.md', + icon: 'icon.svg', + configuration: { + schema: z.object({ + appId: z.string().title('App ID').describe('WeChat Official Account App ID'), + appSecret: z.string().title('App Secret').describe('WeChat Official Account App Secret'), + webhookSigningSecret: z + .string() + .title('WeChat Token') + .describe('WeChat Token used for webhook signature verification'), + }), + }, + channels, + states, + user, +}) diff --git a/integrations/wechat/package.json b/integrations/wechat/package.json new file mode 100644 index 00000000000..71c5f158e3b --- /dev/null +++ b/integrations/wechat/package.json @@ -0,0 +1,21 @@ +{ + "name": "@botpresshub/wechat", + "private": true, + "scripts": { + "build": "bp add -y && bp build", + "check:type": "tsc --noEmit", + "check:bplint": "bp lint" + }, + "dependencies": { + "@botpress/client": "workspace:*", + "@botpress/sdk": "workspace:*", + "axios": "^1.13.6", + "fast-xml-parser": "^5.4.2", + "lodash": "^4.17.21", + "nanoid": "^5.1.5" + }, + "devDependencies": { + "@botpress/cli": "workspace:*", + "@types/lodash": "^4.14.191" + } +} diff --git a/integrations/wechat/src/api/auth.ts b/integrations/wechat/src/api/auth.ts new file mode 100644 index 00000000000..d0b139e03ac --- /dev/null +++ b/integrations/wechat/src/api/auth.ts @@ -0,0 +1,100 @@ +import { RuntimeError } from '@botpress/sdk' +import axios from 'axios' +import { Result } from '../types' +import { usePromiseToResult } from '../utils' +import { WECHAT_API_BASE } from './constants' +import { weChatAuthTokenRespSchema } from './schemas' +import * as bp from '.botpress' + +const MS_PER_SECOND = 1000 +const SECONDS_PER_MINUTE = 60 as const +const TOKEN_EXPIRY_BUFFER = 5 * SECONDS_PER_MINUTE + +type TokenResp = { accessToken: string; expiresAt: number } + +async function _getFreshAccessToken(appId: string, appSecret: string): Promise> { + const tokenIssuedAtMs = Date.now() + const respResult = await axios + .get(`${WECHAT_API_BASE}/token?grant_type=client_credential&appid=${appId}&secret=${appSecret}`) + .then(...usePromiseToResult('Failed to acquire a WeChat access token')) + if (!respResult.success) return respResult + const resp = respResult.data + + const result = weChatAuthTokenRespSchema.safeParse(resp.data) + if (!result.success) { + return { + success: false, + error: new RuntimeError(`Unexpected access token response received -> ${result.error.message}`), + } + } + + const { data } = result + if ('errcode' in data) { + return { + success: false, + error: new RuntimeError( + `Failed to acquire a WeChat access token (Error Code: ${data.errcode}) -> ${data.errmsg}` + ), + } + } + + return { + success: true, + data: { + accessToken: data.access_token, + expiresAt: tokenIssuedAtMs / MS_PER_SECOND + data.expires_in, + }, + } +} + +async function _getCachedAccessToken(client: bp.Client, ctx: bp.Context): Promise> { + const state = await client.getState({ + type: 'integration', + name: 'configuration', + id: ctx.integrationId, + }) + + const { auth = null } = state.state.payload + if (auth === null) { + return { + success: false, + error: new RuntimeError('No access token has been cached'), + } + } + + return { + success: true, + data: auth, + } +} + +const _applyTokenToCache = async (client: bp.Client, ctx: bp.Context, resp: TokenResp) => { + await client.setState({ + type: 'integration', + name: 'configuration', + id: ctx.integrationId, + payload: { + auth: resp, + }, + }) +} + +export const getOrRefreshAccessToken = async ({ client, ctx }: bp.CommonHandlerProps) => { + let tokenResult = await _getCachedAccessToken(client, ctx) + + let cacheToken = false + if (!tokenResult.success || Date.now() / MS_PER_SECOND >= tokenResult.data.expiresAt - TOKEN_EXPIRY_BUFFER) { + const { appId, appSecret } = ctx.configuration + tokenResult = await _getFreshAccessToken(appId, appSecret) + cacheToken = true + } + + if (!tokenResult.success) throw tokenResult.error + const tokenResp = tokenResult.data + + if (cacheToken) { + await _applyTokenToCache(client, ctx, tokenResp) + } + + return tokenResp.accessToken +} diff --git a/integrations/wechat/src/api/axios-helpers.ts b/integrations/wechat/src/api/axios-helpers.ts new file mode 100644 index 00000000000..ad121c2c40b --- /dev/null +++ b/integrations/wechat/src/api/axios-helpers.ts @@ -0,0 +1,105 @@ +import { RuntimeError } from '@botpress/sdk' +import axios, { type AxiosResponse } from 'axios' +import * as bp from '.botpress' + +const NO_CONTENT = 204 + +export async function httpGetAsBuffer(url: string, logger: bp.Logger) { + const resp = await axios.get(url, { + responseType: 'arraybuffer', + transitional: { forcedJSONParsing: false }, + }) + + const content = resp.data + if (content === undefined || content === null) { + const emptyValueType = content === null ? 'null' : 'undefined' + logger.warn(`Sanity check triggered, '${emptyValueType}' returned from axios`) + return null + } + + if (!(content instanceof Buffer)) { + const constructorName = content.constructor?.name ?? '' + // If I understood the Axios docs & configured it correctly, this error should never be thrown + const errorMsg = `Axios did not convert the response body into a Buffer (Constructor Name: ${constructorName})` + throw new RuntimeError(errorMsg) + } + + const rawContentType = _getContentType(resp.headers, resp.status, logger) + const contentType = rawContentType.replace(/([^;]);.+/, '$1') + const charset = _getContentCharset(rawContentType) + + return { data: content, contentType, charset } as const satisfies { + data: Buffer + contentType: string + charset: string + } +} + +/** Performs an Axios get request and parses the response as json based + * on the content-type, otherwise the data is formatted into a buffer. */ +type JsonOrBufferReturn = + | { type: 'JSON'; data: object } + | { type: 'Buffer'; buffer: Buffer; contentType: string; charset: string } +export async function httpGetAsJsonOrBuffer(url: string, logger: bp.Logger): Promise { + const respData = await httpGetAsBuffer(url, logger) + if (respData === null) return null + + const { data, contentType, charset } = respData + if (contentType.includes('application/json')) { + const serializedJSON = _bufferToString(data, charset) + return { type: 'JSON', data: JSON.parse(serializedJSON) } + } + + return { type: 'Buffer', buffer: data, contentType, charset } +} + +type CommonResponseHeadersList = 'Server' | 'Content-Type' | 'Content-Length' | 'Cache-Control' | 'Content-Encoding' +type ResponseHeaderValue = string | string[] | null + +/** Extracts a header value from the Axios headers, and simplifies the output type + * + * @remark This function exists because "AxiosHeaderValue" contains the + * "AxiosHeaders" type which creates an indirect circular type reference. + * @remark The overloads exist strictly for auto-complete */ +export function getHeaderValue(headers: AxiosResponse['headers'], key: CommonResponseHeadersList): ResponseHeaderValue +export function getHeaderValue(headers: AxiosResponse['headers'], key: string): ResponseHeaderValue +export function getHeaderValue(headers: AxiosResponse['headers'], key: string): ResponseHeaderValue { + const headerValue = headers instanceof axios.AxiosHeaders ? headers.get(key) : headers[key] + if (headerValue === null || headerValue === undefined) return null + + if (headerValue instanceof axios.AxiosHeaders) { + throw new RuntimeError("This should never trigger, if it does, IMO it's a bug with the Axios package") + } + + return Array.isArray(headerValue) ? headerValue : `${headerValue}` +} + +const _getContentType = (headers: AxiosResponse['headers'], status: number, logger: bp.Logger) => { + let contentType = getHeaderValue(headers, 'Content-Type') + + if (Array.isArray(contentType)) { + // IMO this should never occur, unless WeChat + // is doing some weird stuff in their Backend + if (contentType.length > 1) { + logger.warn( + `The 'Content-Type' header has multiple values, using first value. All the values are as follows:\n- ${contentType.join('\n- ')}` + ) + } + + contentType = contentType[0] ?? null + } + + if (!contentType) { + if (status === NO_CONTENT) return 'text/plain' + throw new Error(`The 'Content-Type' header has not been set (Status code: ${status})`) + } + return contentType +} + +const _bufferToString = (buffer: Buffer, charset: string = 'utf8') => new TextDecoder(charset).decode(buffer) + +const _getContentCharset = (contentType: string) => { + const pattern = /charset=([^()<>@,;:\"/[\]?.=\s]*)/i + const charset = pattern.test(contentType) ? pattern.exec(contentType)?.[1] : null + return charset?.toLowerCase() ?? 'utf8' +} diff --git a/integrations/wechat/src/api/client.ts b/integrations/wechat/src/api/client.ts new file mode 100644 index 00000000000..cd56b1a575e --- /dev/null +++ b/integrations/wechat/src/api/client.ts @@ -0,0 +1,107 @@ +import { RuntimeError } from '@botpress/client' +import axios from 'axios' +import { useHandleCaughtError } from '../utils' +import { getOrRefreshAccessToken } from './auth' +import { httpGetAsJsonOrBuffer } from './axios-helpers' +import { WECHAT_API_BASE } from './constants' +import { getValidMediaPropOrThrow } from './helpers' +import { + type WeChatSendMessageResp, + wechatSendMessageRespSchema, + wechatUploadMediaRespSchema, + wechatVideoUrlRespSchema, +} from './schemas' +import * as bp from '.botpress' + +type WeChatMediaResponse = Promise<{ content: Buffer; contentType: string }> +type WeChatTextMessage = { msgtype: 'text'; text: { content: string } } +type WeChatImageMessage = { msgtype: 'image'; image: { media_id: string } } +type WeChatVideoMessage = { msgtype: 'video'; video: { media_id: string; title?: string; description?: string } } +type WeChatOutgoingMessage = WeChatTextMessage | WeChatImageMessage | WeChatVideoMessage + +export class WeChatClient { + private constructor( + private readonly _accessToken: string, + private readonly _logger: bp.Logger + ) {} + + public getMediaUrl(accessToken: string, mediaId: string): string { + return `${WECHAT_API_BASE}/media/get?access_token=${accessToken}&media_id=${mediaId}` + } + + public async downloadWeChatMedia(mediaId: string): WeChatMediaResponse { + const mediaUrl = this.getMediaUrl(this._accessToken, mediaId) + return this._downloadWeChatMediaFromUrl(mediaUrl, true) + } + + private async _downloadWeChatMediaFromUrl(url: string, retry: boolean): WeChatMediaResponse { + const resp = await httpGetAsJsonOrBuffer(url, this._logger).catch( + useHandleCaughtError('Failed to download WeChat media') + ) + if (resp === null) { + throw new RuntimeError('Failed to download WeChat media -> No Content') + } + + if (resp.type === 'JSON') { + if (!retry) { + // This solution isn't ideal, I'd rather remove the recursion entirely. But this will suffice for now + throw new RuntimeError('Failed to download media from WeChat -> Too many retries') + } + + const result = wechatVideoUrlRespSchema.safeParse(resp.data) + if (!result.success) { + throw new RuntimeError('Received unexpected response when downloading WeChat media') + } + + const videoUrl = getValidMediaPropOrThrow('video_url', result.data, 'Failed to download media from WeChat') + return this._downloadWeChatMediaFromUrl(videoUrl, false) + } + + return { content: resp.buffer, contentType: resp.contentType } + } + + public async sendMessage(toUser: string, message: WeChatOutgoingMessage): Promise { + const resp = await axios + .post(`${WECHAT_API_BASE}/message/custom/send?access_token=${this._accessToken}`, { + touser: toUser, + ...message, + }) + .catch(useHandleCaughtError('Failed to send WeChat message')) + + const parseResult = wechatSendMessageRespSchema.safeParse(resp.data) + if (!parseResult.success) { + throw new RuntimeError('Unexpected response structure received when attempting to send message to WeChat') + } + + const data = parseResult.data + if (data.errorCode && data.errorCode !== 0) { + throw new RuntimeError(`Failed to send WeChat message -> (Error code: ${data.errorCode}) ${data.errorMsg}`) + } + + return data + } + + public async uploadMedia(mediaType: 'image' | 'voice' | 'video', mediaBlob: Blob, fileExtension: string) { + const formData = new FormData() + formData.append('media', mediaBlob, `media.${fileExtension}`) + + const resp = await axios + .post(`${WECHAT_API_BASE}/media/upload?access_token=${this._accessToken}&type=${mediaType}`, formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }) + .catch(useHandleCaughtError('Failed to upload media to WeChat API')) + + const result = wechatUploadMediaRespSchema.safeParse(resp.data) + if (!result.success) { + throw new RuntimeError(`Unexpected response received when uploading media to WeChat -> ${result.error.message}`) + } + return getValidMediaPropOrThrow('media_id', result.data, 'Failed to upload media to WeChat') + } + + public static async create(props: bp.CommonHandlerProps) { + const token = await getOrRefreshAccessToken(props) + return new WeChatClient(token, props.logger) + } +} diff --git a/integrations/wechat/src/api/constants.ts b/integrations/wechat/src/api/constants.ts new file mode 100644 index 00000000000..b39c88583d4 --- /dev/null +++ b/integrations/wechat/src/api/constants.ts @@ -0,0 +1 @@ +export const WECHAT_API_BASE = 'https://api.weixin.qq.com/cgi-bin' diff --git a/integrations/wechat/src/api/helpers.ts b/integrations/wechat/src/api/helpers.ts new file mode 100644 index 00000000000..4354f16e2cc --- /dev/null +++ b/integrations/wechat/src/api/helpers.ts @@ -0,0 +1,72 @@ +import { RuntimeError } from '@botpress/sdk' +import { useHandleCaughtError } from '../utils' +import { httpGetAsBuffer } from './axios-helpers' +import * as bp from '.botpress' + +const MAX_MEDIA_BYTES = 10 * 1024 * 1024 + +/** @remark This file extension map is not exhaustive. */ +const CONTENT_TYPE_TO_EXTENSION_MAP: Record = { + 'image/jpeg': 'jpg', + 'image/jpg': 'jpg', + 'image/png': 'png', + 'image/gif': 'gif', + 'image/webp': 'webp', + 'image/bmp': 'bmp', +} + +type MediaProp = Partial> +export const getValidMediaPropOrThrow = ( + targetKey: Extract, string>, + data: T & { errcode?: number; errmsg?: string }, + errorMsg: string +) => { + const hasErrorCode = data.errcode && data.errcode !== 0 + const targetValue = data[targetKey] as MediaProp[keyof MediaProp] + if (hasErrorCode || !targetValue) { + const errorSuffix = hasErrorCode ? `(Error code: ${data.errcode}) ${data.errmsg}` : `missing ${targetKey}` + throw new RuntimeError(`${errorMsg} -> ${errorSuffix}`) + } + + return targetValue +} + +/** Converts the content type to a file extension recognized by WeChat. + * + * @remark Expects "contentType" to be striped of additional attributes + * found in the header, such as the charset, BEFORE being passed in. */ +export const convertContentTypeToFileExtension = (contentType: string) => { + const fileExtension = CONTENT_TYPE_TO_EXTENSION_MAP[contentType] + + if (!fileExtension) { + throw new RuntimeError(`Unsupported media content type: ${contentType}`) + } + + return fileExtension +} + +/** Downloads media from a given URL and converts it into a Blob. While + * ensuring it's not too large to be uploaded to WeChat's servers. + * + * @remark The intended use-case is for media not coming from WeChat, + * hense why this function is not directly in the WeChatClient. */ +export const downloadMediaFromURL = async (mediaUrl: string, logger: bp.Logger) => { + const resp = await httpGetAsBuffer(mediaUrl, logger).catch( + useHandleCaughtError(`Failed to download media from URL: '${mediaUrl}'`) + ) + + if (resp === null) { + throw new RuntimeError(`Failed to download media from URL: '${mediaUrl}' -> No content`) + } + + const { data: mediaBuffer, contentType } = resp + const contentLength = mediaBuffer.byteLength + if (Number.isFinite(contentLength) && contentLength > MAX_MEDIA_BYTES) { + throw new RuntimeError(`Media exceeds max size of ${MAX_MEDIA_BYTES} bytes`) + } + + const mediaBlob = new Blob([mediaBuffer], { type: contentType }) + const fileExtension = convertContentTypeToFileExtension(contentType) + + return { mediaBlob, fileExtension } +} diff --git a/integrations/wechat/src/api/schemas.ts b/integrations/wechat/src/api/schemas.ts new file mode 100644 index 00000000000..f16a5c22dfb --- /dev/null +++ b/integrations/wechat/src/api/schemas.ts @@ -0,0 +1,38 @@ +import { z } from '@botpress/sdk' + +const _wechatErrorRespSchema = z.object({ + errcode: z.coerce.number(), + errmsg: z.string(), +}) + +const MISSING_ACCESS_TOKEN_MSG = 'The WeChat access token is missing from the response' +export const weChatAuthTokenRespSchema = z.union([ + _wechatErrorRespSchema, + z.object({ + access_token: z.string({ required_error: MISSING_ACCESS_TOKEN_MSG }).min(1, MISSING_ACCESS_TOKEN_MSG), + expires_in: z.number().min(1).describe('The number of seconds before the access token expires'), + }), +]) + +export const wechatSendMessageRespSchema = _wechatErrorRespSchema + .partial() + .extend({ + // NOTE: AFAIK the "sendMessage" response doesn't contain + // any Message ID, because WeChat doesn't send one back. + // The properties below can likely be safely removed. + msgid: z.string().optional(), + msg_id: z.string().optional(), + message_id: z.string().optional(), + }) + .transform((data) => ({ + errorCode: data.errcode, + errorMsg: data.errmsg, + msgId: data.msgid ?? data.msg_id ?? data.message_id, + })) +export type WeChatSendMessageResp = z.infer + +export const wechatUploadMediaRespSchema = _wechatErrorRespSchema.partial().extend({ media_id: z.string().optional() }) +export type WeChatUploadMediaResp = z.infer + +export const wechatVideoUrlRespSchema = _wechatErrorRespSchema.partial().extend({ video_url: z.string().optional() }) +export type WeChatVideoUrlResp = z.infer diff --git a/integrations/wechat/src/channels/inbound.ts b/integrations/wechat/src/channels/inbound.ts new file mode 100644 index 00000000000..23387a3d574 --- /dev/null +++ b/integrations/wechat/src/channels/inbound.ts @@ -0,0 +1,191 @@ +import { z } from '@botpress/sdk' +import { XMLParser } from 'fast-xml-parser' +import camelCase from 'lodash/camelCase' +import { Result, WebhookResult } from '../types' +import { usePromiseToResult } from '../utils' +import { WeChatMessage, wechatMessageSchema } from './schemas' +import { BaseMessage, createChannelMessage, createMediaMessage } from './utils' +import * as bp from '.botpress' + +export type BotpressConversation = Awaited>['conversation'] +export type BotpressUser = Awaited>['user'] + +export const processInboundChannelMessage = async (props: bp.HandlerProps): Promise> => { + const { client, req } = props + + const requestBody = req.body?.trim() || '' + if (requestBody === '') { + return { success: false, error: new Error('Received empty webhook payload'), status: 200 } + } + + const parseResult = _parseAndValidateMessage(requestBody, props.logger) + if (!parseResult.success) return parseResult + + const wechatMessage: WeChatMessage = parseResult.data + // Yes, the WeChat sendMessage request uses the username as the DM conversation identifier + const wechatConversationId = wechatMessage.fromUserName + const wechatUserId = wechatMessage.fromUserName + + const convResult = await client + .getOrCreateConversation({ + channel: 'channel', + tags: { + id: wechatConversationId, + }, + discriminateByTags: ['id'], + }) + .then(...usePromiseToResult('Failed to create Botpress Conversation')) + if (!convResult.success) return convResult + const { conversation } = convResult.data + + const userResult = await client + .getOrCreateUser({ + tags: { + id: wechatUserId, + }, + discriminateByTags: ['id'], + }) + .then(...usePromiseToResult('Failed to create Botpress User')) + if (!userResult.success) return userResult + const { user } = userResult.data + + const messageId = wechatMessage.msgId + const baseMessage = { + tags: { + id: messageId, + chatId: wechatConversationId, + }, + userId: user.id, + conversationId: conversation.id, + } satisfies BaseMessage + + try { + switch (wechatMessage.msgType) { + case 'text': + if (wechatMessage.content) { + await createChannelMessage(client, baseMessage, 'text', { text: wechatMessage.content }) + } + break + case 'image': + case 'video': + const mediaMessage = { ...wechatMessage, msgType: wechatMessage.msgType } + await createMediaMessage(props, baseMessage, messageId, mediaMessage) + break + case 'voice': + if (wechatMessage.mediaId) { + const voiceText = `[Voice Message] MediaId: ${wechatMessage.mediaId}${wechatMessage.recognition ? `\nRecognized: ${wechatMessage.recognition}` : ''}` + await createChannelMessage(client, baseMessage, 'text', { text: voiceText }) + } + break + case 'location': + const locationText = `[Location] ${wechatMessage.label || 'location'}\nCoordinates: (${wechatMessage.locationX || '0'}, ${wechatMessage.locationY || '0'})` + await createChannelMessage(client, baseMessage, 'text', { text: locationText }) + break + case 'link': + const linkText = `[Link] ${wechatMessage.title || 'Untitled'}\n${wechatMessage.description || ''}\nURL: ${wechatMessage.url || ''}` + await createChannelMessage(client, baseMessage, 'text', { text: linkText }) + break + default: + props.logger.forBot().error(`Unsupported message type: ${wechatMessage.msgType}`) + break + } + } catch (thrown: unknown) { + const errMessage = thrown instanceof Error ? thrown.message : String(thrown) + return { + success: false, + error: new Error(`Failed to process '${wechatMessage.msgType}' message -> ${errMessage}`), + } + } + + return { success: true, data: 'success' } +} + +const _parseAndValidateWeChatXmlMessage = (rawXmlContent: string): Result => { + const parser = new XMLParser({ parseTagValue: false }) + const parseData = _mapWechatMessageKeys(parser.parse(rawXmlContent)) + const result = z.object({ xml: wechatMessageSchema }).safeParse(parseData) + + if (!result.success) { + return { + success: false, + error: new Error(`Unexpected WeChat message body received -> ${result.error.message}`), + } + } + + return { + success: true, + data: result.data.xml, + } +} + +// This abstraction can be removed once we determine the "fallback" to no longer be necessary +const _parseAndValidateMessage = (rawXmlContent: string, logger: bp.Logger): WebhookResult => { + const result = _parseAndValidateWeChatXmlMessage(rawXmlContent) + + if (!result.success) { + const fbResult = _fallbackParseAndValidateWeChatMessage(rawXmlContent) + if (fbResult.success) { + logger.error(`Primary message parser failure, fallback used -> ${result.error.message}`) + return fbResult + } + + return { + success: false, + error: result.error, + status: 200, + } + } + + return result +} + +const _mapWechatMessageKeys = (parsedWechatMessage: Record): Record => { + const mappedEntries = Object.entries(parsedWechatMessage).map(([key, value]) => { + const mappedKey = camelCase(key) + if (typeof value === 'object' && !Array.isArray(value)) { + return [mappedKey, _mapWechatMessageKeys(value)] as const + } + + return [mappedKey, value] as const + }) + return Object.fromEntries(mappedEntries) +} + +/** Tightly coupled to "_fallbackParseAndValidateWeChatMessage" */ +const _extractXmlValue = (xml: string, tag: string): string | undefined => { + // find pattern of CDATA or plain text: or value + const valueRegex = new RegExp(`<${tag}>\\s*(?:)?\\s*`, 'i') + const match = xml.match(valueRegex) + return match?.[1] +} + +// This is a fallback parser, it can be removed once we're sufficiently confident +// that "_parseAndValidateWeChatXmlMessage" works in all expected use-cases. +const _fallbackParseAndValidateWeChatMessage = (reqBody: string): Result => { + const _wechatMessageKeys = [ + 'MsgId', + 'MsgType', + 'ToUserName', + 'FromUserName', + 'Content', + 'PicUrl', + 'MediaId', + 'Recognition', + 'Location_X', + 'Location_Y', + 'Label', + 'Title', + 'Description', + 'Url', + 'CreateTime', + ] + + const entries = _wechatMessageKeys.map((key) => [camelCase(key), _extractXmlValue(reqBody, key)]) + const result = wechatMessageSchema.safeParse(Object.fromEntries(entries)) + + if (!result.success) { + return { success: false, error: new Error(`Unexpected WeChat Message Body received -> ${result.error.message}`) } + } + + return result +} diff --git a/integrations/wechat/src/channels/outbound.ts b/integrations/wechat/src/channels/outbound.ts new file mode 100644 index 00000000000..019df9184df --- /dev/null +++ b/integrations/wechat/src/channels/outbound.ts @@ -0,0 +1,84 @@ +import { RuntimeError } from '@botpress/sdk' +import { nanoid } from 'nanoid' +import { WeChatClient } from '../api/client' +import { downloadMediaFromURL } from '../api/helpers' +import * as bp from '.botpress' + +export const channels = { + channel: { + messages: { + text: async (props) => _handleTextMessage(props), + image: async (props) => _handleImageMessage(props), + video: async (props) => _handleVideoMessage(props), + }, + }, +} satisfies bp.IntegrationProps['channels'] + +type WeChatTextMessage = { msgtype: 'text'; text: { content: string } } +type WeChatImageMessage = { msgtype: 'image'; image: { media_id: string } } +type WeChatVideoMessage = { msgtype: 'video'; video: { media_id: string; title?: string; description?: string } } +type WeChatOutgoingMessage = WeChatTextMessage | WeChatImageMessage | WeChatVideoMessage + +const _handleTextMessage = async (props: bp.MessageProps['channel']['text']) => { + const { payload, logger } = props + try { + await _sendMessageToWeChat(props, { + msgtype: 'text', + text: { content: payload.text }, + }) + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + logger.forBot().error(`Failed to send text message: ${message}`) + } +} + +const _handleImageMessage = async (props: bp.MessageProps['channel']['image']) => { + const { payload, logger } = props + try { + const wechatClient = await WeChatClient.create(props) + const { mediaBlob, fileExtension } = await downloadMediaFromURL(payload.imageUrl, logger) + const mediaId = await wechatClient.uploadMedia('image', mediaBlob, fileExtension) + await _sendMessageToWeChat( + props, + { + msgtype: 'image', + image: { media_id: mediaId }, + }, + wechatClient + ) + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + logger.forBot().error(`Failed to send image message: ${message}`) + } +} + +const _handleVideoMessage = async (props: bp.MessageProps['channel']['video']) => { + const { payload, logger } = props + try { + await _sendMessageToWeChat(props, { + msgtype: 'text', + text: { content: `[Video] ${payload.videoUrl}` }, + }) + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + logger.forBot().error(`Failed to send video message: ${message}`) + } +} + +const _sendMessageToWeChat = async ( + props: bp.AnyMessageProps, + message: WeChatOutgoingMessage, + wechatClient?: WeChatClient +): Promise => { + const { conversation, ack } = props + const wechatConvoId = conversation.tags?.id + if (!wechatConvoId) { + throw new RuntimeError('Conversation does not have a WeChat chat ID') + } + + wechatClient ??= await WeChatClient.create(props) + const sendResponse = await wechatClient.sendMessage(wechatConvoId, message) + + const ackId = sendResponse.msgId ?? `wechat-${nanoid()}` + await ack({ tags: { id: ackId } }) +} diff --git a/integrations/wechat/src/channels/schemas.ts b/integrations/wechat/src/channels/schemas.ts new file mode 100644 index 00000000000..3e702ea811c --- /dev/null +++ b/integrations/wechat/src/channels/schemas.ts @@ -0,0 +1,30 @@ +import { z } from '@botpress/sdk' +import { Merge } from '../types' + +const MS_PER_SECOND = 1000 + +export const wechatMessageSchema = z + .object({ + msgId: z.string().min(1), + msgType: z.string().min(1), + toUserName: z.string().min(1), + fromUserName: z.string().min(1), + content: z.string().optional(), + picUrl: z.string().optional(), + mediaId: z.string().optional(), + recognition: z.string().optional(), + locationX: z.string().optional(), + locationY: z.string().optional(), + label: z.string().optional(), + title: z.string().optional(), + description: z.string().optional(), + url: z.string().optional(), + createTime: z.coerce.number().positive().describe('Seconds since UTC Epoch'), + }) + .passthrough() + .transform(({ createTime, ...data }) => ({ + ...data, + dateCreated: new Date(createTime * MS_PER_SECOND), + })) +export type WeChatMessage = z.infer +export type WeChatMediaMessage = Merge diff --git a/integrations/wechat/src/channels/utils.ts b/integrations/wechat/src/channels/utils.ts new file mode 100644 index 00000000000..8194c6e7e94 --- /dev/null +++ b/integrations/wechat/src/channels/utils.ts @@ -0,0 +1,89 @@ +import { WeChatClient } from '../api/client' +import { WeChatMediaMessage } from './schemas' +import * as bp from '.botpress' + +export type BaseMessage = { + conversationId: string + userId: string + tags: { + id: string + chatId: string + } +} + +type MessageChannels = bp.channels.Channels['channel']['messages'] +type CommonCreateMessageInput = Pick[0], 'type' | 'payload'> +export const createChannelMessage = async ( + client: bp.Client, + baseMessage: BaseMessage, + type: T, + payload: MessageChannels[T] +) => { + await client.createMessage({ + ...baseMessage, + ...({ + type, + payload, + } satisfies CommonCreateMessageInput), + }) +} + +export const createMediaMessage = async ( + props: bp.CommonHandlerProps, + baseMessage: BaseMessage, + messageId: string, + wechatMessage: WeChatMediaMessage +) => { + const { msgType, picUrl, mediaId } = wechatMessage + const mediaUrl = await _getOrUploadWeChatMedia(props, { + messageId, + kind: msgType, + picUrl, + mediaId, + }) + if (!mediaUrl) { + props.logger.forBot().error(`Failed to create message of type: ${msgType}`) + return + } + + const payload = msgType === 'image' ? { imageUrl: mediaUrl } : { videoUrl: mediaUrl } + await createChannelMessage(props.client, baseMessage, msgType, payload) +} + +const _getOrUploadWeChatMedia = async ( + props: bp.CommonHandlerProps, + params: { + messageId: string + mediaId?: string + picUrl?: string + kind: 'image' | 'video' + } +) => { + const { messageId, mediaId, picUrl, kind } = params + const mediaKey = `wechat/media/${kind}/${mediaId || messageId || Date.now()}` + + if (picUrl) { + const { file } = await props.client.uploadFile({ + key: mediaKey, + url: picUrl, + accessPolicies: ['public_content'], + publicContentImmediatelyAccessible: true, + }) + return file.url + } + + if (mediaId) { + const wechatClient = await WeChatClient.create(props) + const { content, contentType } = await wechatClient.downloadWeChatMedia(mediaId) + const { file } = await props.client.uploadFile({ + key: mediaKey, + content, + contentType, + accessPolicies: ['public_content'], + publicContentImmediatelyAccessible: true, + }) + return file.url + } + + return undefined +} diff --git a/integrations/wechat/src/index.ts b/integrations/wechat/src/index.ts new file mode 100644 index 00000000000..84ad3664100 --- /dev/null +++ b/integrations/wechat/src/index.ts @@ -0,0 +1,12 @@ +import { channels } from './channels/outbound' +import { register, unregister } from './setup' +import { handler } from './webhook/handler' +import * as bp from '.botpress' + +export default new bp.Integration({ + register, + unregister, + actions: {}, + channels, + handler, +}) diff --git a/integrations/wechat/src/setup.ts b/integrations/wechat/src/setup.ts new file mode 100644 index 00000000000..12f657d33a4 --- /dev/null +++ b/integrations/wechat/src/setup.ts @@ -0,0 +1,4 @@ +import * as bp from '.botpress' + +export const register: bp.IntegrationProps['register'] = async () => {} +export const unregister: bp.IntegrationProps['unregister'] = async () => {} diff --git a/integrations/wechat/src/types.ts b/integrations/wechat/src/types.ts new file mode 100644 index 00000000000..bddbf117827 --- /dev/null +++ b/integrations/wechat/src/types.ts @@ -0,0 +1,6 @@ +export type Merge = Omit & B + +export type Result = { success: true; data: T } | { success: false; error: Error } + +/** A Result object with a potential status override when a failure occurs */ +export type WebhookResult = { success: true; data: T } | { success: false; error: Error; status?: number } diff --git a/integrations/wechat/src/utils.ts b/integrations/wechat/src/utils.ts new file mode 100644 index 00000000000..55b8d3cd4bd --- /dev/null +++ b/integrations/wechat/src/utils.ts @@ -0,0 +1,21 @@ +import { RuntimeError } from '@botpress/sdk' +import { Result } from './types' + +export const usePromiseToResult = (failMessage: string) => { + return [ + (data: T) => { + return { success: true, data } satisfies Result + }, + (thrown: unknown) => { + const error = thrown instanceof Error ? thrown : new Error(String(thrown)) + return { success: false, error: new RuntimeError(`${failMessage} -> ${error.message}`) } satisfies Result + }, + ] as const +} + +export const useHandleCaughtError = (message: string) => { + return (thrown: unknown) => { + const error = thrown instanceof Error ? thrown : new Error(String(thrown)) + throw new RuntimeError(`${message} -> ${error.message}`) + } +} diff --git a/integrations/wechat/src/webhook/handler.ts b/integrations/wechat/src/webhook/handler.ts new file mode 100644 index 00000000000..eca8f464702 --- /dev/null +++ b/integrations/wechat/src/webhook/handler.ts @@ -0,0 +1,40 @@ +import { Request } from '@botpress/sdk' +import { processInboundChannelMessage } from '../channels/inbound' +import { verifyWebhookSignature } from './signature' +import * as bp from '.botpress' + +export const handler: bp.IntegrationProps['handler'] = async (props) => { + if (!verifyWebhookSignature(props)) { + return _createTextResponse(403, 'Invalid webhook signature') + } + + const reqMethod = props.req.method + if (reqMethod === 'GET') { + return _handleWebhookChallenge(props.req) + } else if (reqMethod === 'POST') { + const result = await processInboundChannelMessage(props) + if (!result.success) { + props.logger.forBot().error(result.error.message) + return _createTextResponse(result.status ?? 500, 'Unexpected payload received') + } + + return _createTextResponse(200, result.data) + } + + props.logger.forBot().warn(`Unhandled request type - ${reqMethod}`) + return _createTextResponse(200, 'OK') +} + +const _handleWebhookChallenge = (req: Request) => { + const query = new URLSearchParams(req.query) + const echostr = query.get('echostr') || '' + return _createTextResponse(200, echostr) +} + +const _createTextResponse = (status: number, body: string) => ({ + status, + headers: { + 'Content-Type': 'text/plain', + }, + body, +}) diff --git a/integrations/wechat/src/webhook/signature.ts b/integrations/wechat/src/webhook/signature.ts new file mode 100644 index 00000000000..9287ae5bdb3 --- /dev/null +++ b/integrations/wechat/src/webhook/signature.ts @@ -0,0 +1,19 @@ +import crypto from 'crypto' +import * as bp from '.botpress' + +const _computeSignature = (signingSecret: string, timestamp: string, nonce: string): string => { + // While I think having a "sort" doesn't make sense, this is simply how "WeChat" implements it + const unhashedSignatureTxt = [signingSecret, timestamp, nonce].sort().join('') + return crypto.createHash('sha1').update(unhashedSignatureTxt, 'utf8').digest('hex') +} + +export const verifyWebhookSignature = ({ req, ctx }: bp.HandlerProps): boolean => { + const query = new URLSearchParams(req.query) + const signature = query.get('signature') + const timestamp = query.get('timestamp') || '' + const nonce = query.get('nonce') || '' + const signingSecret = ctx.configuration.webhookSigningSecret + + const computedSignature = _computeSignature(signingSecret, timestamp, nonce) + return signature === computedSignature +} diff --git a/integrations/wechat/tsconfig.json b/integrations/wechat/tsconfig.json new file mode 100644 index 00000000000..0c1062fd8ce --- /dev/null +++ b/integrations/wechat/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "baseUrl": ".", + "outDir": "dist" + }, + "include": [".botpress/**/*", "definitions/**/*", "src/**/*", "*.ts"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4339a8ef30..7b5b6dc2724 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -356,17 +356,17 @@ importers: dependencies: '@botpress/runtime': specifier: ^1.16.6 - version: 1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.9.3) + version: 1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.8.3) devDependencies: '@botpress/adk': specifier: ^1.16.6 - version: 1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.9.3) + version: 1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.8.3) '@botpress/adk-cli': specifier: ^1.16.6 - version: 1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.9.3)(zod@3.25.76) + version: 1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.8.3)(zod@3.25.76) typescript: specifier: ^5.6.3 - version: 5.9.3 + version: 5.8.3 bots/sheetzy: dependencies: @@ -2089,6 +2089,34 @@ importers: specifier: ^6.9.7 version: 6.9.7 + integrations/wechat: + dependencies: + '@botpress/client': + specifier: workspace:* + version: link:../../packages/client + '@botpress/sdk': + specifier: workspace:* + version: link:../../packages/sdk + axios: + specifier: ^1.13.6 + version: 1.13.6(debug@4.4.3) + fast-xml-parser: + specifier: ^5.4.2 + version: 5.4.2 + lodash: + specifier: ^4.17.21 + version: 4.17.21 + nanoid: + specifier: ^5.1.5 + version: 5.1.5 + devDependencies: + '@botpress/cli': + specifier: workspace:* + version: link:../../packages/cli + '@types/lodash': + specifier: ^4.14.191 + version: 4.17.0 + integrations/whatsapp: dependencies: '@botpress/client': @@ -2177,10 +2205,10 @@ importers: version: 9.0.1 jest: specifier: ^29.5.0 - version: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + version: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) ts-jest: specifier: ^29.1.0 - version: 29.1.0(@babel/core@7.28.0)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.28.0))(jest@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)))(typescript@5.9.3) + version: 29.1.0(@babel/core@7.26.9)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.26.9))(jest@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)))(typescript@5.8.3) integrations/zendesk: dependencies: @@ -2748,7 +2776,7 @@ importers: version: 4.17.21 tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) packages/cognitive: dependencies: @@ -2794,7 +2822,7 @@ importers: version: 11.1.6 tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) packages/common: dependencies: @@ -2958,7 +2986,7 @@ importers: version: 1.2.1(patch_hash=0354139cbc5dbd66e1bc59167ff8e42d3a9a2169038a35f1b7b1b4b843e08a6c) tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) tsx: specifier: ^4.19.2 version: 4.19.2 @@ -2989,7 +3017,7 @@ importers: version: 0.3.0(esbuild@0.25.10) tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) packages/sdk-addons: dependencies: @@ -3022,7 +3050,7 @@ importers: version: 4.17.21 vitest: specifier: ^2 || ^3 || ^4 || ^5 - version: 2.1.8(@types/node@22.16.4)(jsdom@24.1.3)(msw@2.12.0(@types/node@22.16.4)(typescript@5.9.3)) + version: 2.1.8(@types/node@22.16.4)(jsdom@24.1.3)(msw@2.12.0(@types/node@22.16.4)(typescript@5.8.3)) devDependencies: '@botpress/cli': specifier: workspace:* @@ -3044,7 +3072,7 @@ importers: version: 9.3.5 tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) packages/zai: dependencies: @@ -3105,13 +3133,13 @@ importers: version: 4.17.21 msw: specifier: ^2.12.0 - version: 2.12.0(@types/node@22.16.4)(typescript@5.9.3) + version: 2.12.0(@types/node@22.16.4)(typescript@5.8.3) size-limit: specifier: ^11.1.6 version: 11.1.6 tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) packages/zui: devDependencies: @@ -3144,7 +3172,7 @@ importers: version: 4.17.21 tsup: specifier: ^8.0.2 - version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3) + version: 8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3) plugins/analytics: dependencies: @@ -3753,6 +3781,10 @@ packages: resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} engines: {node: '>=6.9.0'} + '@babel/generator@7.27.1': + resolution: {integrity: sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} @@ -3872,6 +3904,11 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@7.27.2': + resolution: {integrity: sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==} + engines: {node: '>=6.0.0'} + hasBin: true + '@babel/parser@7.29.0': resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} engines: {node: '>=6.0.0'} @@ -3986,6 +4023,10 @@ packages: resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} engines: {node: '>=6.9.0'} + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} + '@babel/template@7.28.6': resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} @@ -3994,6 +4035,10 @@ packages: resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} engines: {node: '>=6.9.0'} + '@babel/traverse@7.27.1': + resolution: {integrity: sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==} + engines: {node: '>=6.9.0'} + '@babel/traverse@7.29.0': resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} @@ -8321,8 +8366,8 @@ packages: fast-uri@3.0.2: resolution: {integrity: sha512-GR6f0hD7XXyNJa25Tb9BuIdN0tdr+0BMi6/CJPH3wJO1JjNG3n/VsSw38AwRdKZABm8lGbPfakLRkYzx2V9row==} - fast-xml-builder@1.1.3: - resolution: {integrity: sha512-1o60KoFw2+LWKQu3IdcfcFlGTW4dpqEWmjhYec6H82AYZU2TVBXep6tMl8Z1Y+wM+ZrzCwe3BZ9Vyd9N2rIvmg==} + fast-xml-builder@1.0.0: + resolution: {integrity: sha512-fpZuDogrAgnyt9oDDz+5DBz0zgPdPZz6D4IR7iESxRXElrlGTRkHJ9eEt+SACRJwT0FNFrt71DFQIUFBJfX/uQ==} fast-xml-parser@4.2.5: resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==} @@ -8332,8 +8377,8 @@ packages: resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} hasBin: true - fast-xml-parser@5.5.5: - resolution: {integrity: sha512-NLY+V5NNbdmiEszx9n14mZBseJTC50bRq1VHsaxOmR72JDuZt+5J1Co+dC/4JPnyq+WrIHNM69r0sqf7BMb3Mg==} + fast-xml-parser@5.4.2: + resolution: {integrity: sha512-pw/6pIl4k0CSpElPEJhDppLzaixDEuWui2CUQQBH/ECDf7+y6YwA4Gf7Tyb0Rfe4DIMuZipYj4AEL0nACKglvQ==} hasBin: true fastq@1.15.0: @@ -8478,6 +8523,10 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -10636,10 +10685,6 @@ packages: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} - path-expression-matcher@1.1.3: - resolution: {integrity: sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==} - engines: {node: '>=14.0.0'} - path-is-absolute@1.0.1: resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} engines: {node: '>=0.10.0'} @@ -10867,6 +10912,11 @@ packages: engines: {node: '>=14'} hasBin: true + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + prettier@3.8.1: resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} @@ -12045,11 +12095,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} @@ -13656,6 +13701,14 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 + '@babel/generator@7.27.1': + dependencies: + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.30 + jsesc: 3.1.0 + '@babel/generator@7.29.1': dependencies: '@babel/parser': 7.29.0 @@ -13692,7 +13745,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.26.9) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.27.1 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -13705,7 +13758,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.27.1 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -13714,8 +13767,8 @@ snapshots: '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color @@ -13728,8 +13781,8 @@ snapshots: '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color @@ -13754,7 +13807,7 @@ snapshots: '@babel/core': 7.26.9 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.27.1 transitivePeerDependencies: - supports-color @@ -13763,7 +13816,7 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.27.1 transitivePeerDependencies: - supports-color @@ -13778,7 +13831,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.29.0 + '@babel/types': 7.27.1 '@babel/helper-plugin-utils@7.27.1': {} @@ -13787,7 +13840,7 @@ snapshots: '@babel/core': 7.26.9 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.27.1 transitivePeerDependencies: - supports-color @@ -13796,14 +13849,14 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.29.0 + '@babel/traverse': 7.27.1 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 transitivePeerDependencies: - supports-color @@ -13835,33 +13888,37 @@ snapshots: dependencies: '@babel/types': 7.26.9 + '@babel/parser@7.27.2': + dependencies: + '@babel/types': 7.27.1 + '@babel/parser@7.29.0': dependencies: '@babel/types': 7.29.0 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.0)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.26.9)': @@ -13874,39 +13931,39 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.0)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.0)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.0)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.26.9)': @@ -14013,6 +14070,12 @@ snapshots: '@babel/parser': 7.26.9 '@babel/types': 7.26.9 + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/types': 7.27.1 + '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 @@ -14031,6 +14094,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@7.27.1': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.27.1 + '@babel/parser': 7.27.2 + '@babel/template': 7.27.2 + '@babel/types': 7.27.1 + debug: 4.4.1 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + '@babel/traverse@7.29.0': dependencies: '@babel/code-frame': 7.29.0 @@ -14067,13 +14142,13 @@ snapshots: '@bcoe/v8-coverage@0.2.3': {} - '@botpress/adk-cli@1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.9.3)(zod@3.25.76)': + '@botpress/adk-cli@1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.8.3)(zod@3.25.76)': dependencies: - '@botpress/adk': 1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.9.3) + '@botpress/adk': 1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.8.3) '@botpress/chat': 0.5.5(debug@4.4.3) '@botpress/cli': 5.6.1(@bpinternal/zui@1.3.3)(debug@4.4.3) '@botpress/client': 1.36.0(debug@4.4.3) - '@botpress/runtime': 1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.9.3) + '@botpress/runtime': 1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.8.3) '@botpress/sdk': 5.4.4(@bpinternal/zui@1.3.3)(debug@4.4.3)(esbuild@0.25.10) '@botpress/webchat-client': 0.4.0 '@modelcontextprotocol/sdk': 1.27.1(zod@3.25.76) @@ -14093,7 +14168,7 @@ snapshots: react: 19.2.3 semver: 7.7.4 tar: 7.5.11 - typescript: 5.9.3 + typescript: 5.8.3 transitivePeerDependencies: - '@bpinternal/zui' - '@cfworker/json-schema' @@ -14108,13 +14183,13 @@ snapshots: - utf-8-validate - zod - '@botpress/adk@1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.9.3)': + '@botpress/adk@1.16.6(@bpinternal/zui@1.3.3)(esbuild@0.25.10)(typescript@5.8.3)': dependencies: '@botpress/chat': 0.5.5(debug@4.4.3) '@botpress/cli': 5.6.1(@bpinternal/zui@1.3.3)(debug@4.4.3) '@botpress/client': 1.36.0(debug@4.4.3) '@botpress/cognitive': 0.3.15 - '@botpress/runtime': 1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.9.3) + '@botpress/runtime': 1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.8.3) '@botpress/sdk': 5.4.4(@bpinternal/zui@1.3.3)(debug@4.4.3)(esbuild@0.25.10) '@bpinternal/jex': 1.2.4 '@bpinternal/yargs-extra': 0.0.21 @@ -14127,7 +14202,7 @@ snapshots: prettier: 3.8.1 semver: 7.7.2 ts-morph: 27.0.2 - typescript: 5.9.3 + typescript: 5.8.3 transitivePeerDependencies: - '@bpinternal/zui' - babel-plugin-macros @@ -14154,7 +14229,7 @@ snapshots: jsonwebtoken: 9.0.2 qs: 6.13.0 verror: 1.10.1 - zod: 3.25.76 + zod: 3.24.2 transitivePeerDependencies: - debug @@ -14211,7 +14286,7 @@ snapshots: exponential-backoff: 3.1.1 nanoevents: 9.1.0 - '@botpress/runtime@1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.9.3)': + '@botpress/runtime@1.16.6(debug@4.4.3)(esbuild@0.25.10)(typescript@5.8.3)': dependencies: '@botpress/client': 1.36.0(debug@4.4.3) '@botpress/cognitive': 0.3.15 @@ -14233,7 +14308,7 @@ snapshots: bytes: 3.1.2 dedent: 1.7.2 fast-safe-stringify: 2.1.1 - fast-xml-parser: 5.5.5 + fast-xml-parser: 5.4.2 glob: 11.1.0 llmz: 0.0.54(@botpress/client@1.36.0)(@botpress/cognitive@0.3.15)(@bpinternal/thicktoken@2.0.0)(@bpinternal/zui@1.3.3) lodash: 4.17.21 @@ -14241,7 +14316,7 @@ snapshots: object-sizeof: 2.6.5 p-limit: 7.3.0 pretty-bytes: 7.1.0 - typescript: 5.9.3 + typescript: 5.8.3 ulid: 3.0.2 undici: 7.16.0 transitivePeerDependencies: @@ -14465,8 +14540,8 @@ snapshots: '@doist/todoist-api-typescript@3.0.3(type-fest@5.4.4)': dependencies: - axios: 1.13.1 - axios-case-converter: 1.1.1(axios@1.13.1) + axios: 1.13.6(debug@4.4.3) + axios-case-converter: 1.1.1(axios@1.13.6) axios-retry: 3.9.1 runtypes: 6.7.0 ts-custom-error: 3.3.1 @@ -14935,7 +15010,7 @@ snapshots: jest-util: 29.5.0 slash: 3.0.0 - '@jest/core@29.5.0(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))': + '@jest/core@29.5.0(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))': dependencies: '@jest/console': 29.5.0 '@jest/reporters': 29.5.0 @@ -14949,7 +15024,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.5.0 - jest-config: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + jest-config: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) jest-haste-map: 29.5.0 jest-message-util: 29.5.0 jest-regex-util: 29.4.3 @@ -15040,7 +15115,7 @@ snapshots: '@jest/source-map@29.4.3': dependencies: - '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/trace-mapping': 0.3.30 callsites: 3.1.0 graceful-fs: 4.2.11 @@ -15060,7 +15135,7 @@ snapshots: '@jest/transform@29.5.0': dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@jest/types': 29.5.0 '@jridgewell/trace-mapping': 0.3.29 babel-plugin-istanbul: 6.1.1 @@ -15163,7 +15238,7 @@ snapshots: dependencies: '@types/node': 22.16.4 optionalDependencies: - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) transitivePeerDependencies: - debug @@ -15192,7 +15267,7 @@ snapshots: '@mendable/firecrawl-js@4.3.8': dependencies: - axios: 1.12.2 + axios: 1.13.6(debug@4.4.3) typescript-event-target: 1.1.1 zod: 3.24.2 zod-to-json-schema: 3.24.6(zod@3.24.2) @@ -15779,7 +15854,7 @@ snapshots: '@react-email/render@1.1.2(react-dom@18.3.1(react@19.2.3))(react@19.2.3)': dependencies: html-to-text: 9.0.5 - prettier: 3.8.1 + prettier: 3.5.3 react: 19.2.3 react-dom: 18.3.1(react@19.2.3) react-promise-suspense: 0.3.4 @@ -15958,7 +16033,7 @@ snapshots: '@sendgrid/client@8.1.5': dependencies: '@sendgrid/helpers': 8.0.0 - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) transitivePeerDependencies: - debug @@ -16803,7 +16878,7 @@ snapshots: '@types/axios@0.14.4': dependencies: - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) transitivePeerDependencies: - debug @@ -16989,7 +17064,7 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: '@types/node': 22.16.4 - form-data: 4.0.4 + form-data: 4.0.5 '@types/node@10.17.60': {} @@ -17126,7 +17201,7 @@ snapshots: dependencies: '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.6.3) '@typescript-eslint/types': 8.42.0 - debug: 4.4.3 + debug: 4.4.1 typescript: 5.6.3 transitivePeerDependencies: - supports-color @@ -17219,13 +17294,13 @@ snapshots: msw: 2.12.0(@types/node@22.16.4)(typescript@5.6.3) vite: 5.4.10(@types/node@22.16.4) - '@vitest/mocker@2.1.8(msw@2.12.0(@types/node@22.16.4)(typescript@5.9.3))(vite@5.4.10(@types/node@22.16.4))': + '@vitest/mocker@2.1.8(msw@2.12.0(@types/node@22.16.4)(typescript@5.8.3))(vite@5.4.10(@types/node@22.16.4))': dependencies: '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.12 optionalDependencies: - msw: 2.12.0(@types/node@22.16.4)(typescript@5.9.3) + msw: 2.12.0(@types/node@22.16.4)(typescript@5.8.3) vite: 5.4.10(@types/node@22.16.4) '@vitest/pretty-format@2.1.4': @@ -17538,9 +17613,9 @@ snapshots: aws4@1.12.0: {} - axios-case-converter@1.1.1(axios@1.13.1): + axios-case-converter@1.1.1(axios@1.13.6): dependencies: - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) camel-case: 4.1.2 header-case: 2.0.4 snake-case: 3.0.4 @@ -17548,7 +17623,7 @@ snapshots: axios-error@1.0.4: dependencies: - axios: 0.21.4(debug@4.4.3) + axios: 0.21.4(debug@4.4.1) type-fest: 0.15.1 transitivePeerDependencies: - debug @@ -17573,15 +17648,15 @@ snapshots: axios: 1.6.1 is-retry-allowed: 2.2.0 - axios@0.21.4(debug@4.4.3): + axios@0.21.4(debug@4.4.1): dependencies: - follow-redirects: 1.15.6(debug@4.4.3) + follow-redirects: 1.15.11(debug@4.4.1) transitivePeerDependencies: - debug axios@0.24.0: dependencies: - follow-redirects: 1.15.6(debug@4.4.3) + follow-redirects: 1.15.11(debug@4.4.1) transitivePeerDependencies: - debug @@ -17738,19 +17813,19 @@ snapshots: axios@1.9.0: dependencies: - follow-redirects: 1.15.6(debug@4.4.3) - form-data: 4.0.4 + follow-redirects: 1.15.11(debug@4.4.1) + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - babel-jest@29.5.0(@babel/core@7.28.0): + babel-jest@29.5.0(@babel/core@7.26.9): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@jest/transform': 29.5.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.5.0(@babel/core@7.28.0) + babel-preset-jest: 29.5.0(@babel/core@7.26.9) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -17769,32 +17844,32 @@ snapshots: babel-plugin-jest-hoist@29.5.0: dependencies: - '@babel/template': 7.28.6 - '@babel/types': 7.29.0 + '@babel/template': 7.27.2 + '@babel/types': 7.27.1 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.6 - babel-preset-current-node-syntax@1.0.1(@babel/core@7.28.0): + babel-preset-current-node-syntax@1.0.1(@babel/core@7.26.9): dependencies: - '@babel/core': 7.28.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.0) - - babel-preset-jest@29.5.0(@babel/core@7.28.0): + '@babel/core': 7.26.9 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.9) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.9) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.9) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.9) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.9) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.9) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.9) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.9) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.9) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.9) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.9) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.9) + + babel-preset-jest@29.5.0(@babel/core@7.26.9): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 babel-plugin-jest-hoist: 29.5.0 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.28.0) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.9) bail@1.0.5: {} @@ -17901,7 +17976,7 @@ snapshots: dependencies: '@azure/core-rest-pipeline': 1.22.0 '@azure/msal-node': 2.16.3 - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) botbuilder-core: 4.23.3 botbuilder-stdlib: 4.23.3-internal botframework-connector: 4.23.3 @@ -18599,7 +18674,7 @@ snapshots: docusign-esign@8.4.0: dependencies: '@devhigley/parse-proxy': 1.0.3 - axios: 1.12.2 + axios: 1.13.6(debug@4.4.3) csv-stringify: 1.1.2 jsonwebtoken: 9.0.2 passport-oauth2: 1.8.0 @@ -19368,9 +19443,7 @@ snapshots: fast-uri@3.0.2: {} - fast-xml-builder@1.1.3: - dependencies: - path-expression-matcher: 1.1.3 + fast-xml-builder@1.0.0: {} fast-xml-parser@4.2.5: dependencies: @@ -19380,10 +19453,9 @@ snapshots: dependencies: strnum: 1.0.5 - fast-xml-parser@5.5.5: + fast-xml-parser@5.4.2: dependencies: - fast-xml-builder: 1.1.3 - path-expression-matcher: 1.1.3 + fast-xml-builder: 1.0.0 strnum: 2.2.0 fastq@1.15.0: @@ -19487,6 +19559,10 @@ snapshots: fn.name@1.1.0: {} + follow-redirects@1.15.11(debug@4.4.1): + optionalDependencies: + debug: 4.4.1 + follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: debug: 4.4.3 @@ -19507,6 +19583,11 @@ snapshots: dependencies: is-callable: 1.2.7 + foreground-child@3.1.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -19753,7 +19834,7 @@ snapshots: glob@10.3.10: dependencies: - foreground-child: 3.3.1 + foreground-child: 3.1.1 jackspeak: 2.3.6 minimatch: 9.0.5 minipass: 7.1.2 @@ -20075,7 +20156,7 @@ snapshots: http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -20095,7 +20176,7 @@ snapshots: https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -20463,8 +20544,8 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/core': 7.26.9 + '@babel/parser': 7.27.2 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.0 semver: 6.3.1 @@ -20479,7 +20560,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.3 + debug: 4.4.1 istanbul-lib-coverage: 3.2.0 source-map: 0.6.1 transitivePeerDependencies: @@ -20530,16 +20611,16 @@ snapshots: transitivePeerDependencies: - supports-color - jest-cli@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)): + jest-cli@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)): dependencies: - '@jest/core': 29.5.0(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + '@jest/core': 29.5.0(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) '@jest/test-result': 29.5.0 '@jest/types': 29.5.0 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 import-local: 3.1.0 - jest-config: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + jest-config: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) jest-util: 29.5.0 jest-validate: 29.5.0 prompts: 2.4.2 @@ -20549,12 +20630,12 @@ snapshots: - supports-color - ts-node - jest-config@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)): + jest-config@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)): dependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@jest/test-sequencer': 29.5.0 '@jest/types': 29.5.0 - babel-jest: 29.5.0(@babel/core@7.28.0) + babel-jest: 29.5.0(@babel/core@7.26.9) chalk: 4.1.2 ci-info: 3.8.0 deepmerge: 4.3.1 @@ -20575,7 +20656,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 22.16.4 - ts-node: 10.9.2(@types/node@22.16.4)(typescript@5.9.3) + ts-node: 10.9.2(@types/node@22.16.4)(typescript@5.8.3) transitivePeerDependencies: - supports-color @@ -20735,18 +20816,18 @@ snapshots: jest-snapshot@29.5.0: dependencies: - '@babel/core': 7.28.0 - '@babel/generator': 7.29.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.0) - '@babel/traverse': 7.29.0 - '@babel/types': 7.29.0 + '@babel/core': 7.26.9 + '@babel/generator': 7.27.1 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.26.9) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.26.9) + '@babel/traverse': 7.27.1 + '@babel/types': 7.27.1 '@jest/expect-utils': 29.5.0 '@jest/transform': 29.5.0 '@jest/types': 29.5.0 '@types/babel__traverse': 7.20.6 '@types/prettier': 2.7.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.28.0) + babel-preset-current-node-syntax: 1.0.1(@babel/core@7.26.9) chalk: 4.1.2 expect: 29.5.0 graceful-fs: 4.2.11 @@ -20797,12 +20878,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)): + jest@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)): dependencies: - '@jest/core': 29.5.0(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + '@jest/core': 29.5.0(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) '@jest/types': 29.5.0 import-local: 3.1.0 - jest-cli: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + jest-cli: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) transitivePeerDependencies: - '@types/node' - supports-color @@ -21116,7 +21197,7 @@ snapshots: lodash-es: 4.17.21 lru-cache: 11.0.2 ms: 2.1.3 - prettier: 3.8.1 + prettier: 3.5.3 quickjs-emscripten-core: 0.31.0 ulid: 2.4.0 optionalDependencies: @@ -21498,9 +21579,9 @@ snapshots: '@types/debug': 4.1.12 '@types/lodash': 4.17.0 '@types/url-join': 4.0.3 - axios: 0.21.4(debug@4.4.3) + axios: 0.21.4(debug@4.4.1) camel-case: 4.1.2 - debug: 4.4.3 + debug: 4.4.1 lodash: 4.17.21 map-obj: 4.3.0 pascal-case: 3.1.2 @@ -21515,7 +21596,7 @@ snapshots: '@types/lodash': 4.17.0 '@types/warning': 3.0.3 append-query: 2.1.1 - axios: 0.21.4(debug@4.4.3) + axios: 0.21.4(debug@4.4.1) axios-error: 1.0.4 form-data: 3.0.1 lodash: 4.17.21 @@ -21743,7 +21824,7 @@ snapshots: micromark@2.11.4: dependencies: - debug: 4.4.3 + debug: 4.4.1 parse-entities: 2.0.0 transitivePeerDependencies: - supports-color @@ -21751,7 +21832,7 @@ snapshots: micromark@4.0.2: dependencies: '@types/debug': 4.1.12 - debug: 4.4.3 + debug: 4.4.1 decode-named-character-reference: 1.2.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -21884,7 +21965,7 @@ snapshots: - '@types/node' optional: true - msw@2.12.0(@types/node@22.16.4)(typescript@5.9.3): + msw@2.12.0(@types/node@22.16.4)(typescript@5.8.3): dependencies: '@inquirer/confirm': 5.1.19(@types/node@22.16.4) '@mswjs/interceptors': 0.40.0 @@ -21905,7 +21986,7 @@ snapshots: until-async: 3.0.2 yargs: 17.7.2 optionalDependencies: - typescript: 5.9.3 + typescript: 5.8.3 transitivePeerDependencies: - '@types/node' @@ -22243,7 +22324,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.29.0 + '@babel/code-frame': 7.27.1 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -22295,8 +22376,6 @@ snapshots: path-exists@4.0.0: {} - path-expression-matcher@1.1.3: {} - path-is-absolute@1.0.1: {} path-key@3.1.1: {} @@ -22411,13 +22490,13 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)): + postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)): dependencies: lilconfig: 3.1.3 yaml: 2.7.0 optionalDependencies: postcss: 8.4.47 - ts-node: 10.9.2(@types/node@22.16.4)(typescript@5.9.3) + ts-node: 10.9.2(@types/node@22.16.4)(typescript@5.8.3) postcss@8.4.47: dependencies: @@ -22461,6 +22540,8 @@ snapshots: prettier@3.4.2: {} + prettier@3.5.3: {} + prettier@3.8.1: {} pretty-bytes@7.1.0: {} @@ -23382,7 +23463,7 @@ snapshots: dependencies: component-emitter: 1.3.0 cookiejar: 2.1.4 - debug: 4.4.3 + debug: 4.4.1 fast-safe-stringify: 2.1.1 form-data: 3.0.1 formidable: 1.2.6 @@ -23398,7 +23479,7 @@ snapshots: dependencies: component-emitter: 1.3.0 cookiejar: 2.1.4 - debug: 4.4.3 + debug: 4.4.1 fast-safe-stringify: 2.1.1 form-data: 4.0.5 formidable: 2.1.2 @@ -23585,7 +23666,7 @@ snapshots: trello.js@1.2.7: dependencies: - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) form-data: 4.0.4 tslib: 2.6.2 transitivePeerDependencies: @@ -23613,22 +23694,22 @@ snapshots: dependencies: tslib: 1.14.1 - ts-jest@29.1.0(@babel/core@7.28.0)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.28.0))(jest@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.1.0(@babel/core@7.26.9)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.26.9))(jest@29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)))(typescript@5.8.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + jest: 29.5.0(@types/node@22.16.4)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) jest-util: 29.5.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.6.3 - typescript: 5.9.3 + typescript: 5.8.3 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.28.0 + '@babel/core': 7.26.9 '@jest/types': 29.5.0 - babel-jest: 29.5.0(@babel/core@7.28.0) + babel-jest: 29.5.0(@babel/core@7.26.9) ts-morph@27.0.2: dependencies: @@ -23653,7 +23734,7 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3): + ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 @@ -23667,7 +23748,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.3 + typescript: 5.8.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optional: true @@ -23693,7 +23774,7 @@ snapshots: tslib@2.6.2: {} - tsup@8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3))(typescript@5.9.3): + tsup@8.0.2(@microsoft/api-extractor@7.49.0(@types/node@22.16.4))(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3))(typescript@5.8.3): dependencies: bundle-require: 4.0.2(esbuild@0.19.12) cac: 6.7.14 @@ -23703,7 +23784,7 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.9.3)) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@22.16.4)(typescript@5.8.3)) resolve-from: 5.0.0 rollup: 4.24.2 source-map: 0.8.0-beta.0 @@ -23712,7 +23793,7 @@ snapshots: optionalDependencies: '@microsoft/api-extractor': 7.49.0(@types/node@22.16.4) postcss: 8.4.47 - typescript: 5.9.3 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - ts-node @@ -23763,7 +23844,7 @@ snapshots: twilio@5.11.2: dependencies: - axios: 1.13.1 + axios: 1.13.6(debug@4.4.3) dayjs: 1.11.19 https-proxy-agent: 5.0.1 jsonwebtoken: 9.0.2 @@ -23857,8 +23938,6 @@ snapshots: typescript@5.8.3: {} - typescript@5.9.3: {} - uc.micro@2.1.0: {} uglify-js@3.19.3: @@ -24023,7 +24102,7 @@ snapshots: v8-to-istanbul@9.1.0: dependencies: - '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/trace-mapping': 0.3.30 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.9.0 @@ -24083,7 +24162,7 @@ snapshots: vite-node@2.1.8(@types/node@22.16.4): dependencies: cac: 6.7.14 - debug: 4.4.3 + debug: 4.4.1 es-module-lexer: 1.6.0 pathe: 1.1.2 vite: 5.4.10(@types/node@22.16.4) @@ -24143,10 +24222,10 @@ snapshots: - supports-color - terser - vitest@2.1.8(@types/node@22.16.4)(jsdom@24.1.3)(msw@2.12.0(@types/node@22.16.4)(typescript@5.9.3)): + vitest@2.1.8(@types/node@22.16.4)(jsdom@24.1.3)(msw@2.12.0(@types/node@22.16.4)(typescript@5.8.3)): dependencies: '@vitest/expect': 2.1.8 - '@vitest/mocker': 2.1.8(msw@2.12.0(@types/node@22.16.4)(typescript@5.9.3))(vite@5.4.10(@types/node@22.16.4)) + '@vitest/mocker': 2.1.8(msw@2.12.0(@types/node@22.16.4)(typescript@5.8.3))(vite@5.4.10(@types/node@22.16.4)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.8 '@vitest/snapshot': 2.1.8