From edd902e693f4d4177821c979aefa81d336376173 Mon Sep 17 00:00:00 2001
From: JustinBordage <38659460+JustinBordage@users.noreply.github.com>
Date: Mon, 16 Mar 2026 15:15:47 -0400
Subject: [PATCH] feat(integrations/wechat): implement integration (#14940)
---
integrations/wechat/definitions/channels.ts | 33 ++
integrations/wechat/definitions/index.ts | 3 +
integrations/wechat/definitions/states.ts | 22 +
integrations/wechat/definitions/user.ts | 7 +
integrations/wechat/hub.md | 93 ++++
integrations/wechat/icon.svg | 9 +
integrations/wechat/integration.definition.ts | 24 +
integrations/wechat/package.json | 21 +
integrations/wechat/src/api/auth.ts | 100 ++++
integrations/wechat/src/api/axios-helpers.ts | 105 ++++
integrations/wechat/src/api/client.ts | 107 +++++
integrations/wechat/src/api/constants.ts | 1 +
integrations/wechat/src/api/helpers.ts | 72 +++
integrations/wechat/src/api/schemas.ts | 38 ++
integrations/wechat/src/channels/inbound.ts | 191 ++++++++
integrations/wechat/src/channels/outbound.ts | 84 ++++
integrations/wechat/src/channels/schemas.ts | 30 ++
integrations/wechat/src/channels/utils.ts | 89 ++++
integrations/wechat/src/index.ts | 12 +
integrations/wechat/src/setup.ts | 4 +
integrations/wechat/src/types.ts | 6 +
integrations/wechat/src/utils.ts | 21 +
integrations/wechat/src/webhook/handler.ts | 40 ++
integrations/wechat/src/webhook/signature.ts | 19 +
integrations/wechat/tsconfig.json | 8 +
pnpm-lock.yaml | 451 ++++++++++--------
26 files changed, 1404 insertions(+), 186 deletions(-)
create mode 100644 integrations/wechat/definitions/channels.ts
create mode 100644 integrations/wechat/definitions/index.ts
create mode 100644 integrations/wechat/definitions/states.ts
create mode 100644 integrations/wechat/definitions/user.ts
create mode 100644 integrations/wechat/hub.md
create mode 100644 integrations/wechat/icon.svg
create mode 100644 integrations/wechat/integration.definition.ts
create mode 100644 integrations/wechat/package.json
create mode 100644 integrations/wechat/src/api/auth.ts
create mode 100644 integrations/wechat/src/api/axios-helpers.ts
create mode 100644 integrations/wechat/src/api/client.ts
create mode 100644 integrations/wechat/src/api/constants.ts
create mode 100644 integrations/wechat/src/api/helpers.ts
create mode 100644 integrations/wechat/src/api/schemas.ts
create mode 100644 integrations/wechat/src/channels/inbound.ts
create mode 100644 integrations/wechat/src/channels/outbound.ts
create mode 100644 integrations/wechat/src/channels/schemas.ts
create mode 100644 integrations/wechat/src/channels/utils.ts
create mode 100644 integrations/wechat/src/index.ts
create mode 100644 integrations/wechat/src/setup.ts
create mode 100644 integrations/wechat/src/types.ts
create mode 100644 integrations/wechat/src/utils.ts
create mode 100644 integrations/wechat/src/webhook/handler.ts
create mode 100644 integrations/wechat/src/webhook/signature.ts
create mode 100644 integrations/wechat/tsconfig.json
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*${tag}>`, '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