Commit 91ea5278 by 赵月辉

fix: 修复 fastgpt-merged 项目无法启动的问题

1. pnpm 版本降级 11.7.0→10.6.5 兼容 Node 20
2. 修复 husky prepare 脚本路径
3. 用 FastGPT-main 版本替换过时的 worker 模块(countGptMessagesTokens/htmlStr2Md/readFile)
4. 新增缺失源文件(tokenWorkerConfig.ts, count.ts, base64ImageUpload.ts 等)
5. 添加 OpenTelemetry 依赖到 service
6. 修复 lodash→lodash-es 导入
7. 重写 tool/api.ts 使用 runPluginToolStream 替代废弃的 @fastgpt-sdk/plugin
8. 修复 Turbopack 打包 vm2/proxy-agent 问题
9. 补全缺失环境变量
10. 移除 pnpm-workspace.yaml 中无效的 pro/ 引用和 allowBuilds 配置
parent 5ce0d118
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"private": true, "private": true,
"scripts": { "scripts": {
"dev:pro": "turbo run dev:pro --filter=@fastgpt/app", "dev:pro": "turbo run dev:pro --filter=@fastgpt/app",
"prepare": "husky install", "prepare": "cd .. && husky install fastgpt-merged/.husky",
"gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts", "gen:theme-typings": "chakra-cli tokens packages/web/styles/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
"gen:deploy": "node ./deploy/init.mjs", "gen:deploy": "node ./deploy/init.mjs",
"postinstall": "pnpm gen:theme-typings && pnpm run build:sdks", "postinstall": "pnpm gen:theme-typings && pnpm run build:sdks",
...@@ -55,5 +55,5 @@ ...@@ -55,5 +55,5 @@
"node": ">=20.19.0", "node": ">=20.19.0",
"pnpm": ">=10" "pnpm": ">=10"
}, },
"packageManager": "pnpm@11.7.0" "packageManager": "pnpm@10.6.5"
} }
...@@ -45,7 +45,7 @@ import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils'; ...@@ -45,7 +45,7 @@ import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils';
import { getMCPParentId, getMCPToolRuntimeNode } from '@fastgpt/global/core/app/mcpTools/utils'; import { getMCPParentId, getMCPToolRuntimeNode } from '@fastgpt/global/core/app/mcpTools/utils';
import { AppTypeEnum } from '@fastgpt/global/core/app/constants'; import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { getMCPChildren } from '../mcp'; import { getMCPChildren } from '../mcp';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash-es';
import { UserError } from '@fastgpt/global/common/error/utils'; import { UserError } from '@fastgpt/global/common/error/utils';
type ChildAppType = SystemPluginTemplateItemType & { type ChildAppType = SystemPluginTemplateItemType & {
......
import { RunToolWithStream } from '@fastgpt-sdk/plugin';
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants'; import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
import { pluginClient, BASE_URL, TOKEN } from '../../../thirdProvider/fastgptPlugin'; import {
pluginClient,
PLUGIN_BASE_URL,
runPluginToolStream
} from '../../../thirdProvider/fastgptPlugin';
export async function APIGetSystemToolList() { export async function APIGetSystemToolList() {
// 检查插件服务是否可用 // 检查插件服务是否可用
if (!BASE_URL) { if (!PLUGIN_BASE_URL) {
console.log('Plugin service not configured, returning empty tool list'); console.log('Plugin service not configured, returning empty tool list');
return []; return [];
} }
...@@ -33,13 +36,6 @@ export async function APIGetSystemToolList() { ...@@ -33,13 +36,6 @@ export async function APIGetSystemToolList() {
} }
} }
const runToolInstance = BASE_URL
? new RunToolWithStream({
baseUrl: BASE_URL,
token: TOKEN
})
: null;
export const APIRunSystemTool = async (params: { export const APIRunSystemTool = async (params: {
toolId: string; toolId: string;
inputs: Record<string, any>; inputs: Record<string, any>;
...@@ -65,8 +61,19 @@ export const APIRunSystemTool = async (params: { ...@@ -65,8 +61,19 @@ export const APIRunSystemTool = async (params: {
}; };
onMessage: (message: { type: string; content: string }) => void; onMessage: (message: { type: string; content: string }) => void;
}) => { }) => {
if (!runToolInstance) { if (!PLUGIN_BASE_URL) {
throw new Error('Plugin service not configured'); throw new Error('Plugin service not configured');
} }
return runToolInstance.run(params);
return runPluginToolStream({
pluginId: params.toolId,
input: params.inputs,
systemVar: params.systemVar,
onMessage: (message) => {
params.onMessage({
type: 'stream',
content: JSON.stringify(message)
});
}
});
}; };
...@@ -26,6 +26,10 @@ ...@@ -26,6 +26,10 @@
"@modelcontextprotocol/sdk": "catalog:", "@modelcontextprotocol/sdk": "catalog:",
"@node-rs/jieba": "catalog:", "@node-rs/jieba": "catalog:",
"@opentelemetry/api": "^1.9.0", "@opentelemetry/api": "^1.9.0",
"@opentelemetry/api-logs": "^0.203.0",
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-logs": "^0.203.0",
"@t3-oss/env-core": "catalog:", "@t3-oss/env-core": "catalog:",
"@xmldom/xmldom": "^0.8.10", "@xmldom/xmldom": "^0.8.10",
"@zilliz/milvus2-sdk-node": "2.4.10", "@zilliz/milvus2-sdk-node": "2.4.10",
......
import {
type ChatCompletionContentPart,
type ChatCompletionCreateParams,
type ChatCompletionMessageParam,
type ChatCompletionTool
} from '@fastgpt/global/core/ai/llm/type';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import o200kTokenizer from 'gpt-tokenizer/encoding/o200k_base';
export type CountGptMessagesTokensParams = {
messages: ChatCompletionMessageParam[];
tools?: ChatCompletionTool[];
functionCall?: ChatCompletionCreateParams.Function[];
};
type TokenizerApi = {
countTokens: (
input: string,
options?: {
disallowedSpecial?: Set<string> | 'all';
allowedSpecial?: Set<string> | 'all';
}
) => number;
};
const tokenizer: TokenizerApi = o200kTokenizer;
const noDisallowedSpecial = { disallowedSpecial: new Set<string>() };
/**
* FastGPT 的 worker token 计数统一使用 GPT 现代模型的 o200k_base 编码。
* 该路径只做上下文预算和缺 usage 时的近似兜底;供应商返回 usage 时仍以 usage 为准。
*/
export const GPT_TOKENIZER_ENCODING = 'o200k_base';
type CountableContentPart = ChatCompletionContentPart | { type: 'refusal'; refusal: string };
/**
* 将多模态 content part 转成可计数文本。
*
* 这里不尝试复刻各家模型对图片、音频、文件的精确计费规则,只把会进入上下文或
* 明显影响输入规模的字段纳入估算;真实计费仍以模型供应商返回的 usage 为准。
*/
const contentPartToText = (part: CountableContentPart) => {
if (part.type === 'text') return part.text;
if (part.type === 'image_url') return part.image_url.url;
if (part.type === 'input_audio') return part.input_audio.data;
if (part.type === 'file')
return [part.file.filename, part.file.file_id, part.file.file_data].filter(Boolean).join(' ');
if (part.type === 'file_url') return [part.name, part.url].filter(Boolean).join(' ');
if (part.type === 'refusal') return part.refusal;
return '';
};
/**
* 统一把 OpenAI chat content 规整为字符串。
*
* 字符串 content 直接计数;数组 content 按 part 拼接,保持和旧方案一致的“近似预算”
* 语义,避免在不同消息类型间引入额外分隔符导致历史 token 预算明显漂移。
*/
const contentToText = (content: ChatCompletionMessageParam['content'] = '') => {
if (!content) return '';
if (typeof content === 'string') return content;
return (content as CountableContentPart[]).map(contentPartToText).join('');
};
const countTextTokens = (text: string) => {
try {
return tokenizer.countTokens(text, noDisallowedSpecial);
} catch {
// tokenizer 对极少数非法 special token 组合可能抛错,退回字符数保证计费链路不断。
return text.length;
}
};
/**
* 统计普通 prompt 文本 token 数。
*
* 该函数只在 token worker 内执行,用于知识库裁剪、embedding/rerank 兜底计费等
* 近似场景,统一按 o200k_base 估算。
*/
export const countPromptTokensInWorker = (
prompt: string | ChatCompletionContentPart[] | null | undefined = '',
role: '' | `${ChatCompletionRequestMessageRoleEnum}` = ''
) => {
const promptText =
typeof prompt === 'string' || !prompt ? prompt || '' : prompt.map(contentPartToText).join('');
const text = `${role}\n${promptText}`.trim();
// 兼容旧实现:只有传入 role 时才补 chat message 的固定结构开销。
const supplementaryToken = role ? 4 : 0;
return countTextTokens(text) + supplementaryToken;
};
const countToolsTokens = (tools?: ChatCompletionTool[] | ChatCompletionCreateParams.Function[]) => {
if (!tools || tools.length === 0) return 0;
// 旧方案也是把工具 schema 规整成紧凑文本后估算,避免格式化 JSON 的空白影响预算。
const toolText = JSON.stringify(tools)
.replace(/"/g, '')
.replace(/\n/g, '')
.replace(/( ){2,}/g, ' ');
return countTextTokens(toolText);
};
const getAssistantCallText = (message: ChatCompletionMessageParam) => {
if (message.role !== ChatCompletionRequestMessageRoleEnum.Assistant) return '';
// assistant 的 tool/function call 参数会进入模型上下文,需要和普通 content 一起计入。
const toolCallsText =
message.tool_calls
?.map((item) => `${item?.function?.name} ${item?.function?.arguments}`.trim())
?.join('') || '';
const functionCall = message.function_call;
const functionCallText = `${functionCall?.name || ''} ${functionCall?.arguments || ''}`.trim();
return `${toolCallsText}${functionCallText}`;
};
/**
* 在 token worker 内同步统计 Chat messages token 数。
*
* 这里保持旧实现的消息常数近似规则,只替换为更快的 GPT tokenizer。
* 主线程只通过 worker 调用该函数,避免主进程加载 tokenizer rank 常驻内存。
*/
export const countGptMessagesTokensInWorker = ({
messages,
tools,
functionCall
}: CountGptMessagesTokensParams) => {
return (
messages.reduce((sum, item, index) => {
// 只有最后一条消息的 reasoning_content 会继续影响后续上下文预算。
const reasoningText = index === messages.length - 1 ? item.reasoning_content || '' : '';
const contentPrompt = contentToText(item.content);
const callPrompt = getAssistantCallText(item);
const text = `${item.role}\n${reasoningText}${contentPrompt}${callPrompt}`.trim();
// 每条带 role 的 chat message 保留旧实现的固定结构开销,降低切换 tokenizer 的行为差异。
return sum + countTextTokens(text) + (item.role ? 4 : 0);
}, 0) +
countToolsTokens(tools) +
countToolsTokens(functionCall)
);
};
/* Only the token of gpt-3.5-turbo is used */
import { Tiktoken } from 'tiktoken/lite';
import cl100k_base from './cl100k_base.json';
import { import {
type ChatCompletionMessageParam, type ChatCompletionMessageParam,
type ChatCompletionContentPart,
type ChatCompletionCreateParams, type ChatCompletionCreateParams,
type ChatCompletionTool type ChatCompletionTool
} from '@fastgpt/global/core/ai/type'; } from '@fastgpt/global/core/ai/llm/type';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import { parentPort } from 'worker_threads'; import { parentPort } from 'worker_threads';
import { getLogger, LogCategories } from '../../common/logger';
import { countGptMessagesTokensInWorker, countPromptTokensInWorker } from './count';
const enc = new Tiktoken(cl100k_base.bpe_ranks, cl100k_base.special_tokens, cl100k_base.pat_str); const logger = getLogger(LogCategories.INFRA.WORKER);
/* count messages tokens */ type CountGptMessagesTokensWorkerPayload = {
id: string;
messages?: ChatCompletionMessageParam[];
messageGroups?: ChatCompletionMessageParam[][];
prompts?: (string | null | undefined)[];
tools?: ChatCompletionTool[];
functionCall?: ChatCompletionCreateParams.Function[];
};
/**
* Token 计数 worker 入口。
*
* 单条 messages、批量 messageGroups、批量 prompts 共用同一个 worker 文件,减少 worker
* 类型数量和初始化成本。批量请求在 worker 内同步 map,避免主线程为大量短文本反复
* postMessage,也保证返回顺序和输入顺序一致。
*/
parentPort?.on( parentPort?.on(
'message', 'message',
({ ({
id, id,
messages, messages,
messageGroups,
prompts,
tools, tools,
functionCall functionCall
}: { }: CountGptMessagesTokensWorkerPayload) => {
id: string;
messages: ChatCompletionMessageParam[];
tools?: ChatCompletionTool[];
functionCall?: ChatCompletionCreateParams.Function[];
}) => {
try { try {
/* count one prompt tokens */ const data = (() => {
const countPromptTokens = ( // 上下文裁剪会频繁计算多组 messages,批量放进一次 worker 消息能降低 IPC 开销。
prompt: string | ChatCompletionContentPart[] | null | undefined = '', if (messageGroups) {
role: '' | `${ChatCompletionRequestMessageRoleEnum}` = '' return messageGroups.map((messages) => countGptMessagesTokensInWorker({ messages }));
) => {
const promptText = (() => {
if (!prompt) return '';
if (typeof prompt === 'string') return prompt;
let promptText = '';
prompt.forEach((item) => {
if (item.type === 'text') {
promptText += item.text;
} else if (item.type === 'image_url') {
promptText += item.image_url.url;
}
});
return promptText;
})();
const text = `${role}\n${promptText}`.trim();
try {
const encodeText = enc.encode(text);
const supplementaryToken = role ? 4 : 0;
return encodeText.length + supplementaryToken;
} catch (error) {
return text.length;
} }
};
const countToolsTokens = (
tools?: ChatCompletionTool[] | ChatCompletionCreateParams.Function[]
) => {
if (!tools || tools.length === 0) return 0;
const toolText = tools
? JSON.stringify(tools)
.replace('"', '')
.replace('\n', '')
.replace(/( ){2,}/g, ' ')
: '';
return enc.encode(toolText).length;
};
const total = // embedding/rerank 等路径多为纯文本 prompt,走轻量分支可少做 chat message 拼装。
messages.reduce((sum, item, index) => { if (prompts) {
// Evaluates the text of toolcall and functioncall return prompts.map((prompt) => countPromptTokensInWorker(prompt));
const functionCallPrompt = (() => { }
let prompt = '';
if (item.role === ChatCompletionRequestMessageRoleEnum.Assistant) {
const toolCalls = item.tool_calls;
prompt +=
toolCalls
?.map((item) => `${item?.function?.name} ${item?.function?.arguments}`.trim())
?.join('') || '';
const functionCall = item.function_call;
prompt += `${functionCall?.name} ${functionCall?.arguments}`.trim();
}
return prompt;
})();
const contentPrompt = (() => {
if (!item.content) return '';
if (typeof item.content === 'string') return item.content;
return item.content
.map((item) => {
if (item.type === 'text') return item.text;
return '';
})
.join('');
})();
// Only the last message computed reasoning_text
const reasoningText = index === messages.length - 1 ? item.reasoning_text || '' : '';
return ( return countGptMessagesTokensInWorker({
sum + messages: messages || [],
countPromptTokens(`${reasoningText}${contentPrompt}${functionCallPrompt}`, item.role) tools,
); functionCall
}, 0) + });
countToolsTokens(tools) + })();
countToolsTokens(functionCall);
parentPort?.postMessage({ parentPort?.postMessage({
id, id,
type: 'success', type: 'success',
data: total data
}); });
} catch (error) { } catch (error) {
console.log(error); logger.error('Token count worker failed', { error });
parentPort?.postMessage({ parentPort?.postMessage({
id, id,
type: 'success', type: 'error',
data: 0 data: error instanceof Error ? error.message : String(error)
}); });
} }
} }
......
import { createEnv } from '@t3-oss/env-core';
import z from 'zod';
const IntSchema = z.coerce.number<number>().int().nonnegative();
export const workerEnv = createEnv({
server: {
MAX_HTML_TRANSFORM_CHARS: IntSchema.default(1000000)
},
emptyStringAsUndefined: true,
runtimeEnv: process.env
});
import { parentPort } from 'worker_threads'; import { parentPort } from 'worker_threads';
import { html2md } from './utils'; import { html2md } from './utils';
import { workerResponse } from '../controller'; import {
createWorkerUploadFileHandler,
handleWorkerUploadFileResponse,
isWorkerUploadFileResponse
} from '../utils/uploadFile';
parentPort?.on('message', (params: { html: string }) => { type IncomingMessage = {
try { id: string;
const md = html2md(params?.html || ''); html: string;
uploadImages?: boolean;
type?: 'uploadFileResult' | 'uploadFileError';
requestId?: string;
data?: any;
};
parentPort?.on('message', async (params: IncomingMessage) => {
const { id, html, requestId, data, type } = params;
workerResponse({ if (isWorkerUploadFileResponse(type)) {
parentPort, handleWorkerUploadFileResponse({
status: 'success', taskId: id,
data: md type,
requestId,
data
}); });
} catch (error) { return;
workerResponse({ }
parentPort,
status: 'error', const uploadFileHandler = createWorkerUploadFileHandler({
data: error taskId: id,
parentPort
});
try {
const md = await html2md(html || '', {
uploadFile: params.uploadImages ? uploadFileHandler.uploadFile : undefined
}); });
parentPort?.postMessage({ id, type: 'success', data: md });
} catch (error) {
parentPort?.postMessage({ id, type: 'error', data: error });
} finally {
uploadFileHandler.cleanup();
} }
}); });
import TurndownService from 'turndown'; import TurndownService from 'turndown';
import { type ImageType } from '../readFile/type'; import { simpleMarkdownText } from '@fastgpt/global/common/string/markdown';
import { matchMdImg } from '@fastgpt/global/common/string/markdown'; import { getLogger, LogCategories } from '../../common/logger';
import { getNanoid } from '@fastgpt/global/common/string/tools'; import { workerEnv } from '../env';
// @ts-ignore import { gfm } from 'joplin-turndown-plugin-gfm';
const turndownPluginGfm = require('joplin-turndown-plugin-gfm'); import { uploadBase64Image } from '../utils/base64ImageUpload';
import { type UploadFileHandler } from '../readFile/type';
const processBase64Images = (htmlContent: string) => { import { batchRun } from '@fastgpt/global/common/system/utils';
const base64Regex = /src="data:([^;]+);base64,([^"]+)"/g;
const images: ImageType[] = []; const MAX_HTML_SIZE = workerEnv.MAX_HTML_TRANSFORM_CHARS;
const logger = getLogger(LogCategories.INFRA.WORKER);
const processedHtml = htmlContent.replace(base64Regex, (match, mime, base64Data) => { const htmlBase64UploadConcurrency = 5;
const uuid = `IMAGE_${getNanoid(12)}_IMAGE`;
images.push({ const htmlBase64SrcRegex = /\bsrc\s*=\s*(["'])data:([^;]+);base64,([A-Za-z0-9+/=]+)\1/gi;
uuid,
base64: base64Data, /**
mime * HTML 转 markdown 前实时处理 base64 图片。
}); *
return `src="${uuid}"`; * 有 uploadFile 时上传为对象存储 key;没有 uploadFile 时删除 src,避免大体积 base64
}); * 进入 turndown 或被 worker 结果回传。
*/
const processBase64Images = async (
htmlContent: string,
options: {
uploadFile?: UploadFileHandler;
} = {}
) => {
const matches = Array.from(htmlContent.matchAll(htmlBase64SrcRegex));
if (matches.length === 0) return htmlContent;
const replacements = await batchRun(
matches,
async (match) => {
const [, quote, mime, base64Data] = match;
if (!options.uploadFile) {
return `src=${quote}${quote}`;
}
try {
const { key } = await uploadBase64Image({
mime,
base64: base64Data,
uploadFile: options.uploadFile
});
return `src=${quote}${key}${quote}`;
} catch (error) {
logger.warn('Failed to upload parsed HTML base64 image', { mime, error });
return `src=${quote}${quote}`;
}
},
htmlBase64UploadConcurrency
);
let result = '';
let lastIndex = 0;
for (const [matchIndex, match] of matches.entries()) {
const [fullMatch] = match;
const index = match.index ?? 0;
result += htmlContent.slice(lastIndex, index);
result += replacements[matchIndex];
lastIndex = index + fullMatch.length;
}
return { processedHtml, images }; return result + htmlContent.slice(lastIndex);
}; };
export const html2md = ( export const html2md = async (
html: string html: string,
): { options: {
uploadFile?: UploadFileHandler;
} = {}
): Promise<{
rawText: string; rawText: string;
imageList: ImageType[]; }> => {
} => {
const turndownService = new TurndownService({ const turndownService = new TurndownService({
headingStyle: 'atx', headingStyle: 'atx',
bulletListMarker: '-', bulletListMarker: '-',
...@@ -41,7 +88,7 @@ export const html2md = ( ...@@ -41,7 +88,7 @@ export const html2md = (
try { try {
turndownService.remove(['i', 'script', 'iframe', 'style']); turndownService.remove(['i', 'script', 'iframe', 'style']);
turndownService.use(turndownPluginGfm.gfm); turndownService.use(gfm);
// add custom handling for media tag // add custom handling for media tag
turndownService.addRule('media', { turndownService.addRule('media', {
...@@ -62,19 +109,28 @@ export const html2md = ( ...@@ -62,19 +109,28 @@ export const html2md = (
}); });
// Base64 img to id, otherwise it will occupy memory when going to md // Base64 img to id, otherwise it will occupy memory when going to md
const { processedHtml, images } = processBase64Images(html); const processedHtml = await processBase64Images(html, {
uploadFile: options.uploadFile
});
// if html is too large, return the original html
if (processedHtml.length > MAX_HTML_SIZE) {
return { rawText: processedHtml };
}
const md = turndownService.turndown(processedHtml); const md = turndownService.turndown(processedHtml);
const { text, imageList } = matchMdImg(md);
return { return {
rawText: text, rawText: simpleMarkdownText(md)
imageList: [...images, ...imageList]
}; };
} catch (error) { } catch (error) {
console.log('html 2 markdown error', error); if (options.uploadFile) {
throw error;
}
logger.error('HTML to markdown conversion failed', { error });
return { return {
rawText: '', rawText: ''
imageList: []
}; };
} }
}; };
import iconv from 'iconv-lite'; import iconv from 'iconv-lite';
import { type ReadRawTextByBuffer, type ReadFileResponse } from '../type'; import { type ReadRawTextByBuffer, type ReadFileResponse, type UploadFileHandler } from '../type';
import { matchMdImg } from '@fastgpt/global/common/string/markdown'; import { parseMarkdownBase64Images } from '@fastgpt/global/common/string/markdown';
import { uploadBase64Image } from '../../utils/base64ImageUpload';
const hasNonAsciiByte = (buffer: Buffer) => {
for (let i = 0; i < buffer.length; i++) {
if (buffer[i] > 0x7f) return true;
}
return false;
};
const rawEncodingList = [ const rawEncodingList = [
'ascii', 'ascii',
...@@ -18,30 +26,48 @@ const rawEncodingList = [ ...@@ -18,30 +26,48 @@ const rawEncodingList = [
]; ];
// 加载源文件内容 // 加载源文件内容
export const readFileRawText = async ({ export const readFileRawText = async (
buffer, { buffer, encoding }: ReadRawTextByBuffer,
encoding options: {
}: ReadRawTextByBuffer): Promise<ReadFileResponse> => { uploadFile?: UploadFileHandler;
} = {}
): Promise<ReadFileResponse> => {
const content = (() => { const content = (() => {
try { try {
if (rawEncodingList.includes(encoding)) { const normalizedEncoding = encoding?.toLowerCase?.() || '';
return buffer.toString(encoding as BufferEncoding);
if (rawEncodingList.includes(normalizedEncoding)) {
// `ascii` 只适用于 0x00~0x7F 字节,若含非 ASCII 字节则优先按 utf-8 解码,避免中文乱码
if (normalizedEncoding === 'ascii' && hasNonAsciiByte(buffer)) {
return buffer.toString('utf-8');
}
return buffer.toString(normalizedEncoding as BufferEncoding);
} }
if (encoding) { if (normalizedEncoding) {
return iconv.decode(buffer, encoding); return iconv.decode(buffer, normalizedEncoding);
} }
return buffer.toString('utf-8'); return buffer.toString('utf-8');
} catch (error) { } catch {
return buffer.toString('utf-8'); return buffer.toString('utf-8');
} }
})(); })();
const { text, imageList } = matchMdImg(content); const rawText = await parseMarkdownBase64Images(content, {
controller: (image) => {
if (image.type !== 'base64') return Promise.resolve({ key: '' });
return uploadBase64Image({
mime: image.mime,
base64: image.base64,
uploadFile: options.uploadFile
});
}
});
return { return {
rawText: text, rawText
imageList
}; };
}; };
/**
* 清理表格二维数组中的全空行和全空列,保留原始行列顺序。
* 这里只判断单元格文本是否有有效内容,不做类型转换或业务格式化。
*/
export const filterEmptyTableData = (data: unknown[][]) => {
const filteredRows = data.filter((row) => row.some((cell) => String(cell ?? '').trim() !== ''));
const maxColumnLength = Math.max(0, ...filteredRows.map((row) => row.length));
const columnIndexes = Array.from({ length: maxColumnLength }, (_, index) => index).filter(
(index) => filteredRows.some((row) => String(row[index] ?? '').trim() !== '')
);
return filteredRows.map((row) => columnIndexes.map((index) => row[index] ?? ''));
};
/**
* 转义 Markdown table 单元格中的结构字符,避免单元格内容破坏表格列结构。
*/
export const formatMarkdownTableCell = (cell: unknown) => {
return String(cell ?? '')
.replace(/\r\n|\r|\n/g, '\\n')
.replace(/\|/g, '\\|');
};
export const formatMarkdownTableRow = (row: unknown[]) => {
return `| ${row.map(formatMarkdownTableCell).join(' | ')} |`;
};
import { CUSTOM_SPLIT_SIGN } from '@fastgpt/global/common/string/textSplitter'; import { CUSTOM_SPLIT_SIGN } from '../../../common/string/textSplitter';
import { type ReadRawTextByBuffer, type ReadFileResponse } from '../type'; import { type ReadRawTextByBuffer, type ReadFileResponse } from '../type';
import xlsx from 'node-xlsx'; import XLSX from 'xlsx';
import Papa from 'papaparse'; import { filterEmptyTableData, formatMarkdownTableRow } from './utils';
export const readXlsxRawText = async ({ export const readXlsxRawText = async ({
buffer buffer
}: ReadRawTextByBuffer): Promise<ReadFileResponse> => { }: ReadRawTextByBuffer): Promise<ReadFileResponse> => {
const result = xlsx.parse(buffer, { const workbook = XLSX.read(buffer, {
skipHidden: false, type: 'buffer',
defval: '' cellDates: true
}); });
const result = workbook.SheetNames.map((name) => {
const worksheet = workbook.Sheets[name];
const data = XLSX.utils.sheet_to_json<unknown[]>(worksheet, {
header: 1,
defval: '',
blankrows: true,
raw: false
});
const merges = worksheet['!merges'] ?? [];
const sheetRange = worksheet['!ref'] ? XLSX.utils.decode_range(worksheet['!ref']) : undefined;
const startRow = sheetRange?.s.r ?? 0;
const startColumn = sheetRange?.s.c ?? 0;
if (merges.length > 0) {
// 合并单元格只有左上角存值;!merges 使用 Excel 绝对坐标,
// 但 sheet_to_json 生成的二维数组从 !ref 起点开始,所以填充前要扣掉起始偏移。
// 必须先补齐合并区域,再做空行空列过滤,否则会丢失用户在 Excel 中表达的结构语义。
for (const merge of merges) {
const startDataRow = merge.s.r - startRow;
const startDataColumn = merge.s.c - startColumn;
const endDataRow = merge.e.r - startRow;
const endDataColumn = merge.e.c - startColumn;
const value = data[startDataRow]?.[startDataColumn] ?? '';
if (String(value).trim() === '') continue;
for (let rowIndex = startDataRow; rowIndex <= endDataRow; rowIndex++) {
if (rowIndex < 0) continue;
data[rowIndex] ??= [];
for (let columnIndex = startDataColumn; columnIndex <= endDataColumn; columnIndex++) {
if (columnIndex < 0) continue;
data[rowIndex][columnIndex] = value;
}
}
}
}
return {
name,
data
};
});
const filteredResult = result.map(({ name, data }) => ({
name,
data: filterEmptyTableData(data)
}));
const format2Csv = result.map(({ name, data }) => { const format2Csv = result.map(({ name, data }) => {
return { return {
title: `#${name}`, title: `#${name}`,
...@@ -20,17 +70,14 @@ export const readXlsxRawText = async ({ ...@@ -20,17 +70,14 @@ export const readXlsxRawText = async ({
const rawText = format2Csv.map((item) => item.csvText).join('\n'); const rawText = format2Csv.map((item) => item.csvText).join('\n');
const formatText = result const formatText = filteredResult
.map(({ data }) => { .map(({ data }) => {
const header = data[0]; const header = data[0];
if (!header) return; if (!header) return;
const formatText = `| ${header.join(' | ')} | const formatText = `${formatMarkdownTableRow(header)}
| ${header.map(() => '---').join(' | ')} | | ${header.map(() => '---').join(' | ')} |
${data ${data.slice(1).map(formatMarkdownTableRow).join('\n')}`;
.slice(1)
.map((row) => `| ${row.map((cell) => String(cell).replace(/\n/g, '\\n')).join(' | ')} |`)
.join('\n')}`;
return formatText; return formatText;
}) })
......
import { getNanoid } from '@fastgpt/global/common/string/tools';
import fs from 'fs';
import decompress from 'decompress';
import { DOMParser } from '@xmldom/xmldom'; import { DOMParser } from '@xmldom/xmldom';
import { clearDirFiles } from '../../common/file/utils'; import { type Entry, fromBuffer, type ZipFile } from 'yauzl';
import { addLog } from '../../common/system/log';
const DEFAULTDECOMPRESSSUBLOCATION = '/tmp'; const powerPointXmlPathRegex = /^ppt\/(?:notesSlides\/notesSlide|slides\/slide)\d+\.xml$/;
const powerPointSlidePathRegex = /^ppt\/slides\/slide\d+\.xml$/;
function getNewFileName(ext: string) { const maxPowerPointEntries = 10000;
return `${DEFAULTDECOMPRESSSUBLOCATION}/${getNanoid()}.${ext}`; const maxPowerPointXmlFileBytes = 10 * 1024 * 1024;
} const maxPowerPointXmlBytes = 100 * 1024 * 1024;
const parseString = (xml: string) => {
let parser = new DOMParser();
return parser.parseFromString(xml, 'text/xml');
};
const parsePowerPoint = async ({ const parsePowerPoint = async ({
filepath, buffer,
decompressPath,
encoding encoding
}: { }: {
filepath: string; buffer: Buffer;
decompressPath: string;
encoding: BufferEncoding; encoding: BufferEncoding;
}) => { }) => {
// Files regex that hold our content of interest const decodeXml = (data: Buffer) => {
const allFilesRegex = /ppt\/(notesSlides|slides)\/(notesSlide|slide)\d+.xml/g; try {
const slidesRegex = /ppt\/slides\/slide\d+.xml/g; return data.toString(encoding);
} catch {
/** The decompress location which contains the filename in it */ // 上游按整个压缩包探测编码时可能得到 Node.js 不支持的编码名。
return data.toString('utf-8');
const files = await decompress(filepath, decompressPath, { }
filter: (x) => !!x.path.match(allFilesRegex) };
const zip = await new Promise<ZipFile>((resolve, reject) => {
fromBuffer(
buffer,
{
lazyEntries: true,
decodeStrings: true,
validateEntrySizes: true,
strictFileNames: true
},
(error, zipFile) => {
if (error) {
reject(error);
return;
}
resolve(zipFile);
}
);
}); });
// Verify if atleast the slides xml files exist in the extracted files list. const files = await new Promise<{ path: string; content: string }[]>((resolve, reject) => {
if ( const result: { path: string; content: string }[] = [];
files.length == 0 || let entriesRead = 0;
!files.map((file) => file.path).some((filename) => filename.match(slidesRegex)) let totalXmlBytes = 0;
) { let settled = false;
return Promise.reject('解析 PPT 失败');
} const fail = (error: unknown) => {
if (settled) return;
settled = true;
zip.close();
reject(error);
};
const readEntryContent = (entry: Entry) => {
zip.openReadStream(entry, (error, stream) => {
if (error) {
fail(error);
return;
}
const chunks: Buffer[] = [];
let entryBytes = 0;
stream.on('data', (chunk: Buffer) => {
entryBytes += chunk.length;
totalXmlBytes += chunk.length;
// 元数据可被伪造,必须按流中真实输出字节中止解压。
if (entryBytes > maxPowerPointXmlFileBytes || totalXmlBytes > maxPowerPointXmlBytes) {
stream.destroy(new Error('解析 PPT 失败'));
return;
}
chunks.push(chunk);
});
stream.once('error', fail);
stream.once('end', () => {
if (settled) return;
result.push({
path: entry.fileName,
content: decodeXml(Buffer.concat(chunks, entryBytes))
});
zip.readEntry();
});
});
};
zip.once('error', fail);
zip.on('entry', (entry: Entry) => {
entriesRead += 1;
if (entriesRead > maxPowerPointEntries) {
fail('解析 PPT 失败');
return;
}
// Returning an array of all the xml contents read using fs.readFileSync const createdOnUnix = entry.versionMadeBy >>> 8 === 3;
const xmlContentArray = await Promise.all( const unixMode = entry.externalFileAttributes >>> 16;
files.map(async (file) => { const unixFileType = unixMode & 0xf000;
try { const isRegularFile =
return await fs.promises.readFile(`${decompressPath}/${file.path}`, encoding); !entry.fileName.endsWith('/') &&
} catch (err) { (!createdOnUnix || unixFileType === 0 || unixFileType === 0x8000);
return await fs.promises.readFile(`${decompressPath}/${file.path}`, 'utf-8');
// yauzl 在 entry 事件前校验原始路径;这里只读取锚定 OOXML 路径下的普通文件。
if (!isRegularFile || !powerPointXmlPathRegex.test(entry.fileName)) {
zip.readEntry();
return;
} }
})
);
let responseArr: string[] = []; if (
entry.uncompressedSize > maxPowerPointXmlFileBytes ||
totalXmlBytes + entry.uncompressedSize > maxPowerPointXmlBytes
) {
fail('解析 PPT 失败');
return;
}
readEntryContent(entry);
});
zip.once('end', () => {
if (settled) return;
settled = true;
if (!result.some((file) => powerPointSlidePathRegex.test(file.path))) {
reject('解析 PPT 失败');
return;
}
resolve(result);
});
xmlContentArray.forEach((xmlContent) => { if (zip.entryCount > maxPowerPointEntries) {
/** Find text nodes with a:p tags */ fail('解析 PPT 失败');
const xmlParagraphNodesList = parseString(xmlContent).getElementsByTagName('a:p'); return;
}
zip.readEntry();
});
const sortedFiles = files.sort((a, b) => {
const getSlideNumber = (path: string) => {
const match = path.match(/\d+/);
return match ? parseInt(match[0]) : 0;
};
return getSlideNumber(a.path) - getSlideNumber(b.path);
});
const parser = new DOMParser();
/** Store all the text content to respond */ return sortedFiles
responseArr.push( .map(({ content }) => {
Array.from(xmlParagraphNodesList) const xmlParagraphNodesList = parser
// Filter paragraph nodes than do not have any text nodes which are identifiable by a:t tag .parseFromString(content, 'text/xml')
.getElementsByTagName('a:p');
return Array.from(xmlParagraphNodesList)
.filter((paragraphNode) => paragraphNode.getElementsByTagName('a:t').length != 0) .filter((paragraphNode) => paragraphNode.getElementsByTagName('a:t').length != 0)
.map((paragraphNode) => { .map((paragraphNode) => {
/** Find text nodes with a:t tags */
const xmlTextNodeList = paragraphNode.getElementsByTagName('a:t'); const xmlTextNodeList = paragraphNode.getElementsByTagName('a:t');
return Array.from(xmlTextNodeList) return Array.from(xmlTextNodeList)
.filter((textNode) => textNode.childNodes[0] && textNode.childNodes[0].nodeValue) .filter((textNode) => textNode.childNodes[0] && textNode.childNodes[0].nodeValue)
.map((textNode) => textNode.childNodes[0].nodeValue) .map((textNode) => textNode.childNodes[0].nodeValue)
.join(''); .join('');
}) })
.join('\n') .join('\n');
); })
}); .join('\n');
return responseArr.join('\n');
}; };
/**
* 解析受支持的 Office 文件文本。
* PPTX 归档按 entry 流式读取,只解压固定 OOXML 路径下且满足大小限制的普通 XML 文件。
*/
export const parseOffice = async ({ export const parseOffice = async ({
buffer, buffer,
encoding, encoding,
...@@ -89,43 +176,10 @@ export const parseOffice = async ({ ...@@ -89,43 +176,10 @@ export const parseOffice = async ({
encoding: BufferEncoding; encoding: BufferEncoding;
extension: string; extension: string;
}) => { }) => {
// Prepare file for processing switch (extension) {
// create temp file subdirectory if it does not exist case 'pptx':
if (!fs.existsSync(DEFAULTDECOMPRESSSUBLOCATION)) { return parsePowerPoint({ buffer, encoding });
fs.mkdirSync(DEFAULTDECOMPRESSSUBLOCATION, { recursive: true }); default:
} return Promise.reject('只能读取 .pptx 文件');
// temp file name
const filepath = getNewFileName(extension);
const decompressPath = `${DEFAULTDECOMPRESSSUBLOCATION}/${getNanoid()}`;
// const decompressPath = `${DEFAULTDECOMPRESSSUBLOCATION}/test`;
// write new file
try {
fs.writeFileSync(filepath, buffer, {
encoding
});
} catch (err) {
fs.writeFileSync(filepath, buffer, {
encoding: 'utf-8'
});
} }
const text = await (async () => {
try {
switch (extension) {
case 'pptx':
return parsePowerPoint({ filepath, decompressPath, encoding });
default:
return Promise.reject('只能读取 .pptx 文件');
}
} catch (error) {
addLog.error(`Load ppt error`, { error });
}
return '';
})();
fs.unlinkSync(filepath);
clearDirFiles(decompressPath);
return text;
}; };
...@@ -12,6 +12,17 @@ export type ImageType = { ...@@ -12,6 +12,17 @@ export type ImageType = {
mime: string; mime: string;
}; };
export type UploadedFileResult = {
key: string;
previewUrl?: string;
};
export type UploadFileHandler = (data: {
name: string;
mime: string;
buffer: ArrayBuffer;
}) => Promise<UploadedFileResult>;
export type ReadFileResponse = { export type ReadFileResponse = {
rawText: string; rawText: string;
formatText?: string; formatText?: string;
......
import { availableParallelism, cpus } from 'os';
/**
* Token 计算 worker 数量跟随当前运行环境可用 CPU 数,最多保留 4 个。
*
* tokenizer 会在每个 worker 内各自加载一份编码表,worker 过多会放大常驻内存;
* 这里固定为 min(cpu, 4),不再暴露配置项,避免部署环境误配过多 worker。
* availableParallelism 会优先考虑容器 CPU 配额,拿不到时再回退到物理 CPU 数。
*/
export const getTokenWorkerCount = () => {
const availableCpu = availableParallelism?.() || cpus().length || 1;
return Math.max(1, Math.min(availableCpu, 4));
};
import { type UploadFileHandler } from '../readFile/type';
import { resolveMimeExtension } from '../../common/s3/utils/mime';
import { getLogger, LogCategories } from '../../common/logger';
const logger = getLogger(LogCategories.INFRA.WORKER);
const MAX_PARSED_IMAGE_BUFFER_SIZE = 40 * 1024 * 1024;
export class ParsedImageTooLargeError extends Error {
constructor(size: number, maxSize: number) {
super(`Parsed image too large. Size: ${size} bytes, maximum allowed: ${maxSize} bytes`);
this.name = 'ParsedImageTooLargeError';
}
}
const getBase64DecodedSize = (base64: string) => {
const normalizedBase64 = base64.replace(/\s/g, '');
const padding = normalizedBase64.endsWith('==') ? 2 : normalizedBase64.endsWith('=') ? 1 : 0;
return Math.max(0, Math.floor((normalizedBase64.length * 3) / 4) - padding);
};
const toTransferableArrayBuffer = (buffer: Buffer): ArrayBuffer => {
if (
buffer.buffer instanceof ArrayBuffer &&
buffer.byteOffset === 0 &&
buffer.byteLength === buffer.buffer.byteLength
) {
return buffer.buffer;
}
const imageArrayBuffer = new Uint8Array(buffer.byteLength);
imageArrayBuffer.set(buffer);
return imageArrayBuffer.buffer;
};
export const uploadBase64Image = async ({
mime,
base64,
uploadFile
}: {
mime: string;
base64: string;
uploadFile?: UploadFileHandler;
}) => {
if (!uploadFile) {
logger.warn('Missing image upload handler when parsing document image', { mime });
throw new Error('Missing imageKeyOptions.prefix for parsed document image upload');
}
const decodedSize = getBase64DecodedSize(base64);
if (decodedSize > MAX_PARSED_IMAGE_BUFFER_SIZE) {
throw new ParsedImageTooLargeError(decodedSize, MAX_PARSED_IMAGE_BUFFER_SIZE);
}
const imageBuffer = Buffer.from(base64, 'base64');
const filename = `${crypto.randomUUID()}${resolveMimeExtension(mime)}`;
return uploadFile({
name: filename,
mime,
buffer: toTransferableArrayBuffer(imageBuffer)
}).catch((error) => {
logger.warn('Failed to upload parsed document image from worker', {
filename,
mime,
error
});
throw error;
});
};
import type { MessagePort } from 'worker_threads';
import { type UploadFileHandler, type UploadedFileResult } from '../readFile/type';
type WorkerUploadFileResponse = {
id: string;
type?: string;
requestId?: string;
data?: UploadedFileResult | any;
};
type PendingUploadFileRequest = {
taskId: string;
resolve: (value: UploadedFileResult) => void;
reject: (error: any) => void;
};
const pendingUploadFileRequests = new Map<string, PendingUploadFileRequest>();
export const isWorkerUploadFileResponse = (type?: string) =>
type === 'uploadFileResult' || type === 'uploadFileError';
/**
* 处理 worker uploadFile 的主线程回包。
*
* 多个 worker 入口共用同一套 requestId -> Promise 映射,按 taskId 防串扰。
*/
export const handleWorkerUploadFileResponse = ({
taskId,
type,
requestId,
data
}: {
taskId: string;
type?: string;
requestId?: string;
data?: any;
}) => {
if (!isWorkerUploadFileResponse(type) || !requestId) return false;
const pending = pendingUploadFileRequests.get(requestId);
if (!pending || pending.taskId !== taskId) return true;
pendingUploadFileRequests.delete(requestId);
if (type === 'uploadFileError') {
pending.reject(data);
} else {
pending.resolve(data as UploadedFileResult);
}
return true;
};
export const cleanupWorkerUploadFileRequests = (taskId: string, reason: Error) => {
for (const [requestId, pending] of pendingUploadFileRequests.entries()) {
if (pending.taskId !== taskId) continue;
pending.reject(reason);
pendingUploadFileRequests.delete(requestId);
}
};
/**
* 为 worker 内单个任务创建 uploadFile handler。
*
* 返回的 handler 会向主线程发送 `uploadFile` 中间事件,并等待同 requestId 的结果回包;
* cleanup 必须在任务结束时调用,避免 dangling promise。
*/
export const createWorkerUploadFileHandler = ({
taskId,
parentPort
}: {
taskId: string;
parentPort?: MessagePort | null;
}): {
uploadFile: UploadFileHandler;
cleanup: () => void;
} => {
const uploadFile: UploadFileHandler = (data) =>
new Promise((resolve, reject) => {
const requestId = crypto.randomUUID();
pendingUploadFileRequests.set(requestId, { taskId, resolve, reject });
try {
parentPort?.postMessage(
{
id: taskId,
type: 'uploadFile',
requestId,
data
},
[data.buffer]
);
} catch (error) {
pendingUploadFileRequests.delete(requestId);
reject(error);
}
});
return {
uploadFile,
cleanup: () =>
cleanupWorkerUploadFileRequests(
taskId,
new Error('Worker upload request cancelled before completion')
)
};
};
/**
* 兼容需要在 worker 入口内部监听回包的任务,例如 readFile 会忽略主 message handler 中的回包。
*/
export const createWorkerUploadFileHandlerWithListener = ({
taskId,
parentPort,
enabled
}: {
taskId: string;
parentPort?: MessagePort | null;
enabled: boolean;
}): {
uploadFile?: UploadFileHandler;
cleanup: () => void;
} => {
if (!enabled) return { cleanup: () => {} };
const onMessage = ({ id, type, requestId, data }: WorkerUploadFileResponse) => {
if (id !== taskId) return;
handleWorkerUploadFileResponse({
taskId,
type,
requestId,
data
});
};
parentPort?.on('message', onMessage);
const bridge = createWorkerUploadFileHandler({ taskId, parentPort });
return {
uploadFile: bridge.uploadFile,
cleanup: () => {
parentPort?.off('message', onMessage);
bridge.cleanup();
}
};
};
...@@ -256,7 +256,7 @@ importers: ...@@ -256,7 +256,7 @@ importers:
version: 9.39.5(jiti@2.7.0) version: 9.39.5(jiti@2.7.0)
eslint-config-next: eslint-config-next:
specifier: catalog:lint specifier: catalog:lint
version: 16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) version: 16.2.6(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)
husky: husky:
specifier: ^8.0.3 specifier: ^8.0.3
version: 8.0.3 version: 8.0.3
...@@ -274,7 +274,7 @@ importers: ...@@ -274,7 +274,7 @@ importers:
version: 10.4.3(socks@2.8.9) version: 10.4.3(socks@2.8.9)
next-i18next: next-i18next:
specifier: 'catalog:' specifier: 'catalog:'
version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
prettier: prettier:
specifier: catalog:lint specifier: catalog:lint
version: 3.8.3 version: 3.8.3
...@@ -526,6 +526,18 @@ importers: ...@@ -526,6 +526,18 @@ importers:
'@opentelemetry/api': '@opentelemetry/api':
specifier: ^1.9.0 specifier: ^1.9.0
version: 1.9.1 version: 1.9.1
'@opentelemetry/api-logs':
specifier: ^0.203.0
version: 0.203.0
'@opentelemetry/exporter-logs-otlp-http':
specifier: ^0.203.0
version: 0.203.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources':
specifier: ^2.0.1
version: 2.10.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-logs':
specifier: ^0.203.0
version: 0.203.0(@opentelemetry/api@1.9.1)
'@t3-oss/env-core': '@t3-oss/env-core':
specifier: 'catalog:' specifier: 'catalog:'
version: 0.13.10(typescript@6.0.3)(zod@4.4.3) version: 0.13.10(typescript@6.0.3)(zod@4.4.3)
...@@ -733,7 +745,7 @@ importers: ...@@ -733,7 +745,7 @@ importers:
version: 2.2.4(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 2.2.4(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0) version: 2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0)
'@chakra-ui/react': '@chakra-ui/react':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0) version: 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0)
...@@ -814,7 +826,7 @@ importers: ...@@ -814,7 +826,7 @@ importers:
version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0) version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0)
next-i18next: next-i18next:
specifier: 'catalog:' specifier: 'catalog:'
version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
papaparse: papaparse:
specifier: ^5.4.1 specifier: ^5.4.1
version: 5.5.4 version: 5.5.4
...@@ -887,7 +899,7 @@ importers: ...@@ -887,7 +899,7 @@ importers:
version: 2.2.4(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 2.2.4(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0) version: 2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0)
'@chakra-ui/react': '@chakra-ui/react':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0) version: 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0)
...@@ -1046,7 +1058,7 @@ importers: ...@@ -1046,7 +1058,7 @@ importers:
version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0) version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0)
next-i18next: next-i18next:
specifier: 'catalog:' specifier: 'catalog:'
version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
nprogress: nprogress:
specifier: ^0.2.0 specifier: ^0.2.0
version: 0.2.0 version: 0.2.0
...@@ -1170,7 +1182,7 @@ importers: ...@@ -1170,7 +1182,7 @@ importers:
version: 9.39.5(jiti@1.21.7) version: 9.39.5(jiti@1.21.7)
eslint-config-next: eslint-config-next:
specifier: catalog:lint specifier: catalog:lint
version: 16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3) version: 16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
tailwindcss: tailwindcss:
specifier: ^3 specifier: ^3
version: 3.4.19(tsx@4.23.8)(yaml@2.9.0) version: 3.4.19(tsx@4.23.8)(yaml@2.9.0)
...@@ -1267,7 +1279,7 @@ importers: ...@@ -1267,7 +1279,7 @@ importers:
version: 2.2.4(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 2.2.4(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
'@chakra-ui/next-js': '@chakra-ui/next-js':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0) version: 2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0)
'@chakra-ui/react': '@chakra-ui/react':
specifier: 'catalog:' specifier: 'catalog:'
version: 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0) version: 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0)
...@@ -1312,7 +1324,7 @@ importers: ...@@ -1312,7 +1324,7 @@ importers:
version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0) version: 16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0)
next-i18next: next-i18next:
specifier: 'catalog:' specifier: 'catalog:'
version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0) version: 15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0)
react: react:
specifier: ^18 specifier: ^18
version: 18.0.0 version: 18.0.0
...@@ -1343,7 +1355,7 @@ importers: ...@@ -1343,7 +1355,7 @@ importers:
version: 9.39.5(jiti@2.7.0) version: 9.39.5(jiti@2.7.0)
eslint-config-next: eslint-config-next:
specifier: catalog:lint specifier: catalog:lint
version: 16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) version: 16.2.6(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)
postcss: postcss:
specifier: 'catalog:' specifier: 'catalog:'
version: 8.5.25 version: 8.5.25
...@@ -4204,12 +4216,6 @@ packages: ...@@ -4204,12 +4216,6 @@ packages:
peerDependencies: peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0' '@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/core@1.17.0':
resolution: {integrity: sha512-tfnl3h+UefCgx1aeN2xtrmr6BmdWGKXypk0pflQR0urFS40aE88trnkOMc2HTJZbMrqEEl4HsaBeFhwLVXsrJg==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.7.0'
'@opentelemetry/core@1.24.0': '@opentelemetry/core@1.24.0':
resolution: {integrity: sha512-FP2oN7mVPqcdxJDTTnKExj4mi91EH+DNuArKfHTjPuJWe2K1JfMIVXNfahw1h3onJxQnxS8K0stKkogX05s+Aw==} resolution: {integrity: sha512-FP2oN7mVPqcdxJDTTnKExj4mi91EH+DNuArKfHTjPuJWe2K1JfMIVXNfahw1h3onJxQnxS8K0stKkogX05s+Aw==}
engines: {node: '>=14'} engines: {node: '>=14'}
...@@ -4433,12 +4439,6 @@ packages: ...@@ -4433,12 +4439,6 @@ packages:
peerDependencies: peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.10.0' '@opentelemetry/api': '>=1.0.0 <1.10.0'
'@opentelemetry/resources@1.17.0':
resolution: {integrity: sha512-+u0ciVnj8lhuL/qGRBPeVYvk7fL+H/vOddfvmOeJaA1KC+5/3UED1c9KoZQlRsNT5Kw1FaK8LkY2NVLYfOVZQw==}
engines: {node: '>=14'}
peerDependencies:
'@opentelemetry/api': '>=1.0.0 <1.7.0'
'@opentelemetry/resources@1.24.0': '@opentelemetry/resources@1.24.0':
resolution: {integrity: sha512-mxC7E7ocUS1tLzepnA7O9/G8G6ZTdjCH2pXme1DDDuCuk6n2/53GADX+GWBuyX0dfIxeMInIbJAdjlfN9GNr6A==} resolution: {integrity: sha512-mxC7E7ocUS1tLzepnA7O9/G8G6ZTdjCH2pXme1DDDuCuk6n2/53GADX+GWBuyX0dfIxeMInIbJAdjlfN9GNr6A==}
engines: {node: '>=14'} engines: {node: '>=14'}
...@@ -4578,10 +4578,6 @@ packages: ...@@ -4578,10 +4578,6 @@ packages:
peerDependencies: peerDependencies:
'@opentelemetry/api': '>=1.3.0 <1.10.0' '@opentelemetry/api': '>=1.3.0 <1.10.0'
'@opentelemetry/semantic-conventions@1.17.0':
resolution: {integrity: sha512-+fguCd2d8d2qruk0H0DsCEy2CTK3t0Tugg7MhZ/UQMvmewbZLNnJ6heSYyzIZWG5IPfAXzoj4f4F/qpM7l4VBA==}
engines: {node: '>=14'}
'@opentelemetry/semantic-conventions@1.24.0': '@opentelemetry/semantic-conventions@1.24.0':
resolution: {integrity: sha512-yL0jI6Ltuz8R+Opj7jClGrul6pOoYrdfVmzQS4SITXRPH7I5IRZbrwe/6/v8v4WYMa6MYZG480S1+uc/IGfqsA==} resolution: {integrity: sha512-yL0jI6Ltuz8R+Opj7jClGrul6pOoYrdfVmzQS4SITXRPH7I5IRZbrwe/6/v8v4WYMa6MYZG480S1+uc/IGfqsA==}
engines: {node: '>=14'} engines: {node: '>=14'}
...@@ -14975,7 +14971,7 @@ snapshots: ...@@ -14975,7 +14971,7 @@ snapshots:
'@chakra-ui/react': 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0) '@chakra-ui/react': 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0)
react: 18.0.0 react: 18.0.0
'@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0)': '@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react@18.0.0)':
dependencies: dependencies:
'@chakra-ui/react': 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0) '@chakra-ui/react': 2.10.10(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(react@18.0.0))(@types/react@18.3.31)(framer-motion@9.1.7(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react-dom@18.0.0(react@18.0.0))(react@18.0.0)
'@emotion/cache': 11.14.0 '@emotion/cache': 11.14.0
...@@ -15925,7 +15921,7 @@ snapshots: ...@@ -15925,7 +15921,7 @@ snapshots:
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
dependencies: dependencies:
string-width: 5.1.2 string-width: 5.1.2
string-width-cjs: string-width@4.2.0 string-width-cjs: string-width@4.2.3
strip-ansi: 7.2.0 strip-ansi: 7.2.0
strip-ansi-cjs: strip-ansi@6.0.1 strip-ansi-cjs: strip-ansi@6.0.1
wrap-ansi: 8.1.0 wrap-ansi: 8.1.0
...@@ -16789,11 +16785,6 @@ snapshots: ...@@ -16789,11 +16785,6 @@ snapshots:
dependencies: dependencies:
'@opentelemetry/api': 1.9.1 '@opentelemetry/api': 1.9.1
'@opentelemetry/core@1.17.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/semantic-conventions': 1.17.0
'@opentelemetry/core@1.24.0(@opentelemetry/api@1.9.1)': '@opentelemetry/core@1.24.0(@opentelemetry/api@1.9.1)':
dependencies: dependencies:
'@opentelemetry/api': 1.9.1 '@opentelemetry/api': 1.9.1
...@@ -17100,12 +17091,6 @@ snapshots: ...@@ -17100,12 +17091,6 @@ snapshots:
'@opentelemetry/api': 1.9.1 '@opentelemetry/api': 1.9.1
'@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1) '@opentelemetry/core': 2.7.1(@opentelemetry/api@1.9.1)
'@opentelemetry/resources@1.17.0(@opentelemetry/api@1.9.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@opentelemetry/core': 1.17.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.17.0
'@opentelemetry/resources@1.24.0(@opentelemetry/api@1.9.1)': '@opentelemetry/resources@1.24.0(@opentelemetry/api@1.9.1)':
dependencies: dependencies:
'@opentelemetry/api': 1.9.1 '@opentelemetry/api': 1.9.1
...@@ -17284,8 +17269,6 @@ snapshots: ...@@ -17284,8 +17269,6 @@ snapshots:
'@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 2.10.0(@opentelemetry/api@1.9.1)
'@opentelemetry/semantic-conventions': 1.43.0 '@opentelemetry/semantic-conventions': 1.43.0
'@opentelemetry/semantic-conventions@1.17.0': {}
'@opentelemetry/semantic-conventions@1.24.0': {} '@opentelemetry/semantic-conventions@1.24.0': {}
'@opentelemetry/semantic-conventions@1.43.0': {} '@opentelemetry/semantic-conventions@1.43.0': {}
...@@ -19124,10 +19107,10 @@ snapshots: ...@@ -19124,10 +19107,10 @@ snapshots:
dependencies: dependencies:
'@types/node': 20.19.43 '@types/node': 20.19.43
'@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)': '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)':
dependencies: dependencies:
'@eslint-community/regexpp': 4.12.2 '@eslint-community/regexpp': 4.12.2
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
'@typescript-eslint/scope-manager': 8.66.0 '@typescript-eslint/scope-manager': 8.66.0
'@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
...@@ -19290,7 +19273,7 @@ snapshots: ...@@ -19290,7 +19273,7 @@ snapshots:
obug: 2.1.4 obug: 2.1.4
std-env: 4.2.0 std-env: 4.2.0
tinyrainbow: 3.1.1 tinyrainbow: 3.1.1
vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(@vitest/coverage-v8@4.1.10)(vite@8.2.0(@types/node@20.19.43)(esbuild@0.28.1)(jiti@2.7.0)(sass@1.102.0)(tsx@4.23.8)(yaml@2.9.0)) vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@20.19.43)(@vitest/coverage-v8@4.1.10)(vite@8.2.0(@types/node@20.19.43)(esbuild@0.25.11)(jiti@1.21.7)(sass@1.102.0)(tsx@4.23.8)(yaml@2.9.0))
'@vitest/expect@4.1.10': '@vitest/expect@4.1.10':
dependencies: dependencies:
...@@ -21565,13 +21548,13 @@ snapshots: ...@@ -21565,13 +21548,13 @@ snapshots:
optionalDependencies: optionalDependencies:
source-map: 0.6.1 source-map: 0.6.1
eslint-config-next@16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3): eslint-config-next@16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3):
dependencies: dependencies:
'@next/eslint-plugin-next': 16.2.6 '@next/eslint-plugin-next': 16.2.6
eslint: 9.39.5(jiti@1.21.7) eslint: 9.39.5(jiti@1.21.7)
eslint-import-resolver-node: 0.3.10 eslint-import-resolver-node: 0.3.10
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@1.21.7)) eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@1.21.7))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@1.21.7)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@1.21.7))
eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@1.21.7)) eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@1.21.7))
eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@1.21.7)) eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@1.21.7))
...@@ -21584,13 +21567,13 @@ snapshots: ...@@ -21584,13 +21567,13 @@ snapshots:
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
- supports-color - supports-color
eslint-config-next@16.2.6(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3): eslint-config-next@16.2.6(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3):
dependencies: dependencies:
'@next/eslint-plugin-next': 16.2.6 '@next/eslint-plugin-next': 16.2.6
eslint: 9.39.5(jiti@2.7.0) eslint: 9.39.5(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10 eslint-import-resolver-node: 0.3.10
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-react: 7.37.5(eslint@9.39.5(jiti@2.7.0))
eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-react-hooks: 7.1.1(eslint@9.39.5(jiti@2.7.0))
...@@ -21611,12 +21594,12 @@ snapshots: ...@@ -21611,12 +21594,12 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-import-resolver-typescript@3.5.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@1.21.7)): eslint-import-resolver-typescript@3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@1.21.7)):
dependencies: dependencies:
debug: 4.4.3 debug: 4.4.3
enhanced-resolve: 5.24.5 enhanced-resolve: 5.24.5
eslint: 9.39.5(jiti@1.21.7) eslint: 9.39.5(jiti@1.21.7)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7))
get-tsconfig: 4.14.1 get-tsconfig: 4.14.1
globby: 13.1.2 globby: 13.1.2
is-core-module: 2.16.2 is-core-module: 2.16.2
...@@ -21630,7 +21613,7 @@ snapshots: ...@@ -21630,7 +21613,7 @@ snapshots:
debug: 4.4.3 debug: 4.4.3
enhanced-resolve: 5.24.5 enhanced-resolve: 5.24.5
eslint: 9.39.5(jiti@2.7.0) eslint: 9.39.5(jiti@2.7.0)
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)) eslint-plugin-import: 2.32.0(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0))
get-tsconfig: 4.14.1 get-tsconfig: 4.14.1
globby: 13.1.2 globby: 13.1.2
is-core-module: 2.16.2 is-core-module: 2.16.2
...@@ -21639,29 +21622,28 @@ snapshots: ...@@ -21639,29 +21622,28 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-module-utils@2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7)): eslint-module-utils@2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7)):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
eslint: 9.39.5(jiti@1.21.7) eslint: 9.39.5(jiti@1.21.7)
eslint-import-resolver-node: 0.3.10 eslint-import-resolver-node: 0.3.10
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@1.21.7)) eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@1.21.7))
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-module-utils@2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)): eslint-module-utils@2.14.0(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)
eslint: 9.39.5(jiti@2.7.0) eslint: 9.39.5(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10 eslint-import-resolver-node: 0.3.10
eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0)) eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.32.0)(eslint@9.39.5(jiti@2.7.0))
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7)): eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7)):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.9 array-includes: 3.1.9
...@@ -21672,7 +21654,7 @@ snapshots: ...@@ -21672,7 +21654,7 @@ snapshots:
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 9.39.5(jiti@1.21.7) eslint: 9.39.5(jiti@1.21.7)
eslint-import-resolver-node: 0.3.10 eslint-import-resolver-node: 0.3.10
eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7)) eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@1.21.7))
hasown: 2.0.4 hasown: 2.0.4
is-core-module: 2.16.2 is-core-module: 2.16.2
is-glob: 4.0.3 is-glob: 4.0.3
...@@ -21684,13 +21666,13 @@ snapshots: ...@@ -21684,13 +21666,13 @@ snapshots:
string.prototype.trimend: 1.0.10 string.prototype.trimend: 1.0.10
tsconfig-paths: 3.15.0 tsconfig-paths: 3.15.0
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
transitivePeerDependencies: transitivePeerDependencies:
- eslint-import-resolver-typescript - eslint-import-resolver-typescript
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
- supports-color - supports-color
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)): eslint-plugin-import@2.32.0(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.9 array-includes: 3.1.9
...@@ -21701,7 +21683,7 @@ snapshots: ...@@ -21701,7 +21683,7 @@ snapshots:
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 9.39.5(jiti@2.7.0) eslint: 9.39.5(jiti@2.7.0)
eslint-import-resolver-node: 0.3.10 eslint-import-resolver-node: 0.3.10
eslint-module-utils: 2.14.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0)) eslint-module-utils: 2.14.0(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.5.2)(eslint@9.39.5(jiti@2.7.0))
hasown: 2.0.4 hasown: 2.0.4
is-core-module: 2.16.2 is-core-module: 2.16.2
is-glob: 4.0.3 is-glob: 4.0.3
...@@ -21712,8 +21694,6 @@ snapshots: ...@@ -21712,8 +21694,6 @@ snapshots:
semver: 6.3.1 semver: 6.3.1
string.prototype.trimend: 1.0.10 string.prototype.trimend: 1.0.10
tsconfig-paths: 3.15.0 tsconfig-paths: 3.15.0
optionalDependencies:
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3)
transitivePeerDependencies: transitivePeerDependencies:
- eslint-import-resolver-typescript - eslint-import-resolver-typescript
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
...@@ -25088,7 +25068,7 @@ snapshots: ...@@ -25088,7 +25068,7 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
next-i18next@15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0): next-i18next@15.4.2(@types/react@18.3.31)(i18next@23.16.8)(next@16.2.6(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(babel-plugin-macros@3.1.0)(react-dom@18.0.0(react@18.0.0))(react@18.0.0)(sass@1.102.0))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.0.0(react@18.0.0))(react@18.0.0))(react@18.0.0):
dependencies: dependencies:
'@babel/runtime': 7.29.7 '@babel/runtime': 7.29.7
'@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.31) '@types/hoist-non-react-statics': 3.3.7(@types/react@18.3.31)
...@@ -25425,7 +25405,7 @@ snapshots: ...@@ -25425,7 +25405,7 @@ snapshots:
'@opentelemetry/exporter-logs-otlp-grpc': 0.51.0(@opentelemetry/api-logs@0.51.0)(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-logs-otlp-grpc': 0.51.0(@opentelemetry/api-logs@0.51.0)(@opentelemetry/api@1.9.1)
'@opentelemetry/exporter-logs-otlp-http': 0.51.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-logs-otlp-http': 0.51.0(@opentelemetry/api@1.9.1)
'@opentelemetry/exporter-logs-otlp-proto': 0.51.0(@opentelemetry/api@1.9.1) '@opentelemetry/exporter-logs-otlp-proto': 0.51.0(@opentelemetry/api@1.9.1)
'@opentelemetry/resources': 1.17.0(@opentelemetry/api@1.9.1) '@opentelemetry/resources': 1.24.0(@opentelemetry/api@1.9.1)
'@opentelemetry/sdk-logs': 0.51.0(@opentelemetry/api-logs@0.51.0)(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-logs': 0.51.0(@opentelemetry/api-logs@0.51.0)(@opentelemetry/api@1.9.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@opentelemetry/api' - '@opentelemetry/api'
...@@ -27819,7 +27799,7 @@ snapshots: ...@@ -27819,7 +27799,7 @@ snapshots:
typescript-eslint@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3): typescript-eslint@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3):
dependencies: dependencies:
'@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3))(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
'@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
'@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3) '@typescript-eslint/typescript-estree': 8.66.0(typescript@6.0.3)
'@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3) '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@1.21.7))(typescript@6.0.3)
......
...@@ -6,27 +6,11 @@ packages: ...@@ -6,27 +6,11 @@ packages:
- projects/marketplace - projects/marketplace
- projects/mcp_server - projects/mcp_server
- projects/volume-manager - projects/volume-manager
- pro/llm_benchmark/content_benchmark
- pro/admin
- pro/sso
- pro/browser-sandbox
- document/ - document/
- scripts/icon - scripts/icon
- sdk/* - sdk/*
allowBuilds:
'@google/genai': set this to true or false
'@parcel/watcher': set this to true or false
core-js: set this to true or false
esbuild: set this to true or false
micromark: set this to true or false
mongodb-memory-server: set this to true or false
msgpackr-extract: set this to true or false
protobufjs: set this to true or false
sharp: set this to true or false
vue-demi: set this to true or false
catalog: catalog:
'@chakra-ui/anatomy': ^2 '@chakra-ui/anatomy': ^2
'@chakra-ui/icons': ^2 '@chakra-ui/icons': ^2
......
...@@ -10,6 +10,10 @@ DB_MAX_LINK=5 ...@@ -10,6 +10,10 @@ DB_MAX_LINK=5
TOKEN_KEY=dfdasfdas TOKEN_KEY=dfdasfdas
# 文件阅读时的秘钥 # 文件阅读时的秘钥
FILE_TOKEN_KEY=filetokenkey FILE_TOKEN_KEY=filetokenkey
# 密钥加密 key
AES256_SECRET_KEY=fastgptsecret
# Invoke 反向调用 JWT 密钥,至少 32 位
INVOKE_TOKEN_SECRET=fastgpt_invoke_token_secret_32_chars_min
# root key, 最高权限 # root key, 最高权限
ROOT_KEY=fdafasd ROOT_KEY=fdafasd
# openai 基本地址,可用作中转。 # openai 基本地址,可用作中转。
......
...@@ -77,7 +77,9 @@ const nextConfig: NextConfig = { ...@@ -77,7 +77,9 @@ const nextConfig: NextConfig = {
'@zilliz/milvus2-sdk-node', '@zilliz/milvus2-sdk-node',
'@opentelemetry/api-logs', '@opentelemetry/api-logs',
'@mariozechner/pi-agent-core', '@mariozechner/pi-agent-core',
'@mariozechner/pi-ai' '@mariozechner/pi-ai',
'proxy-agent',
'vm2'
], ],
// 优化大库的 barrel exports tree-shaking // 优化大库的 barrel exports tree-shaking
experimental: { experimental: {
......
# fastgpt-merged 项目启动修复记录
# fastgpt-merged 项目启动修复记录
## 项目背景
`fastgpt-merged` 是将二次开发版本(`fastgpt`)的定制功能与官方最新版本(`FastGPT-main`)架构进行整合的合并版本。合并后存在大量错误,无法启动。
## 问题总览
| 问题类型 | 数量 | 严重程度 |
|----------|------|----------|
| package.json 文件丢失 | 15 个 | 🔴 致命 |
| pnpm-workspace.yaml 配置无效 | 2 处 | 🔴 致命 |
| Worker 源文件过时 | 4 个模块 | 🔴 致命 |
| 缺失源文件 | 2 个 | 🟡 阻塞 |
| 依赖缺失 | 多个 | 🟡 阻塞 |
| 环境变量缺失 | 2 个 | 🟡 阻塞 |
| Turbopack 打包问题 | 1 个 | 🟡 阻塞 |
---
## 修复详情
### 1. package.json 文件丢失(从 git 仓库拉取恢复)
**原因**:上传时 `.gitignore` 过滤了所有 `package.json` 文件。
**修复**:从 git 远程仓库 `git pull origin master` 拉取更新,恢复了全部 15 个 `package.json`:
| # | 文件路径 | 包名 |
|---|---------|------|
| 1 | `package.json` | `AI Agents Flow` |
| 2 | `document/package.json` | `@fastgpt/document` |
| 3 | `packages/global/package.json` | `@fastgpt/global` |
| 4 | `packages/next/package.json` | `@fastgpt/next` |
| 5 | `packages/service/package.json` | `@fastgpt/service` |
| 6 | `packages/web/package.json` | `@fastgpt/web` |
| 7 | `projects/app/package.json` | `@fastgpt/app` |
| 8 | `projects/code-sandbox/package.json` | `@fastgpt/code-sandbox` |
| 9 | `projects/marketplace/package.json` | `@fastgpt/marketplace` |
| 10 | `projects/mcp_server/package.json` | `@fastgpt/mcp_server` |
| 11 | `projects/volume-manager/package.json` | `@fastgpt/volume-manager` |
| 12 | `scripts/icon/package.json` | `@fastgpt/icon-scripts` |
| 13 | `sdk/otel/package.json` | `@fastgpt-sdk/otel` |
| 14 | `sdk/sandbox-adapter/package.json` | `@fastgpt-sdk/sandbox-adapter` |
| 15 | `sdk/storage/package.json` | `@fastgpt-sdk/storage` |
### 2. pnpm 版本兼容性修复
**原因**:原始 `package.json` 指定 `"packageManager": "pnpm@11.7.0"`,但 pnpm 11 需要 Node.js 22+,当前环境为 Node.js v20.20.2,导致 `ERR_UNKNOWN_BUILTIN_MODULE: node:sqlite` 错误。
**修复**:将 `package.json` 中的 `packageManager` 降级为 `"pnpm@10.6.5"`。
**文件**:`package.json`
### 3. husky prepare 脚本修复
**原因**:`.git` 目录在项目上级目录(`Upgrade-lufa-fastgpt/`),而 `fastgpt-merged/` 是子目录。`husky install` 默认在当前目录查找 `.git`,导致 `husky - .git can't be found` 错误。
**修复**:
```json
// 修改前
"prepare": "husky install"
// 修改后
"prepare": "cd .. && husky install fastgpt-merged/.husky"
```
**文件**:`package.json`
### 4. Worker 源文件过时(用 FastGPT-main 版本替换)
合并时部分 worker 模块仍使用旧版代码,引用了不存在的依赖或已废弃的 API。用 FastGPT-main 的最新版本替换了以下文件:
#### 4.1 countGptMessagesTokens 模块
**问题**:
- `index.ts` 使用 `tiktoken/lite` 和 `cl100k_base.json`(旧版依赖,未安装)
- 缺少 `count.ts` 文件(FastGPT-main 新增)
**修复**:
- 替换 `worker/countGptMessagesTokens/index.ts` → 新版使用 `gpt-tokenizer/o200k_base`,支持批量 `messageGroups` 和 `prompts`
- 创建 `worker/countGptMessagesTokens/count.ts` → 核心计数逻辑
#### 4.2 htmlStr2Md 模块
**问题**:
- `utils.ts` 导入不存在的 `matchMdImg`(旧版 API)
- `index.ts` 不支持异步图片上传
**修复**:
- 替换 `worker/htmlStr2Md/index.ts` → 新版支持 `uploadImages` 参数和 base64 图片上传
- 替换 `worker/htmlStr2Md/utils.ts` → 新版使用 `simpleMarkdownText` 和 `processBase64Images`
- 新增 `worker/utils/base64ImageUpload.ts` → base64 图片上传工具
- 新增 `worker/env.ts` → worker 环境变量配置
- 更新 `worker/readFile/type.d.ts` → 新增 `UploadFileHandler` 类型
#### 4.3 readFile 模块
**问题**:
- `extension/xlsx.ts` 使用 `node-xlsx`(未安装的旧依赖)
- `parseOffice.ts` 使用 `decompress`(未安装的旧依赖)
- `extension/rawText.ts` 导入不存在的 `matchMdImg`
**修复**:
- 替换 `extension/xlsx.ts` → 新版使用 `xlsx`(SheetJS),支持合并单元格填充
- 替换 `parseOffice.ts` → 新版使用 `yauzl` 流式读取,无需临时文件
- 替换 `extension/rawText.ts` → 新版使用 `parseMarkdownBase64Images`
- 新增 `extension/utils.ts` → 表格数据处理工具函数
### 5. OpenTelemetry 依赖补全
**原因**:`packages/service/common/otel/log/index.ts` 被 `common/system/log.ts` 引用(11 个文件依赖),需要 `@opentelemetry/sdk-logs`、`@opentelemetry/exporter-logs-otlp-http`、`@opentelemetry/resources` 等依赖,但原始 `packages/service/package.json` 中未声明。
**修复**:在 `packages/service/package.json` 的 dependencies 中添加:
```json
"@opentelemetry/api-logs": "^0.203.0",
"@opentelemetry/exporter-logs-otlp-http": "^0.203.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-logs": "^0.203.0",
```
**文件**:`packages/service/package.json`
### 6. lodash 导入修复
**原因**:`packages/service/core/app/plugin/controller.ts:48` 导入 `lodash/cloneDeep`,但 `lodash` 未安装(项目使用 `lodash-es`)。
**修复**:
```typescript
// 修改前
import { cloneDeep } from 'lodash';
// 修改后
import { cloneDeep } from 'lodash-es';
```
**文件**:`packages/service/core/app/plugin/controller.ts`
### 7. @fastgpt-sdk/plugin 导入修复
**原因**:`packages/service/core/app/tool/api.ts` 从旧版拷贝,导入了 `@fastgpt-sdk/plugin`(已废弃的包),但 `fastgptPlugin/index.ts` 已升级为新版使用 `runPluginToolStream`。
**修复**:重写 `api.ts`,使用新的 `runPluginToolStream` 替代 `RunToolWithStream`:
```typescript
import { pluginClient, PLUGIN_BASE_URL, runPluginToolStream } from '../../../thirdProvider/fastgptPlugin';
```
**文件**:`packages/service/core/app/tool/api.ts`
### 8. 缺失的 tokenWorkerConfig.ts
**原因**:合并时从 FastGPT-main 拷贝了 `tiktoken/index.ts`(引用 `tokenWorkerConfig`),但漏拷了 `tokenWorkerConfig.ts` 文件。
**修复**:创建 `packages/service/worker/tokenWorkerConfig.ts`,导出 `getTokenWorkerCount` 函数。
### 9. Turbopack 打包问题(vm2/proxy-agent)
**原因**:`proxy-agent` 的传递依赖 `vm2` 在 Turbopack 打包时产生 `/ROOT/node_modules/...` 错误路径。
**修复**:在 `projects/app/next.config.ts` 的 `serverExternalPackages` 中添加 `proxy-agent` 和 `vm2`,阻止 Turbopack 打包这些模块。
**文件**:`projects/app/next.config.ts`
### 10. 环境变量补全
**原因**:`.env.local` 中缺少 `AES256_SECRET_KEY` 和 `INVOKE_TOKEN_SECRET`。
**修复**:在 `.env.local` 中添加:
```env
AES256_SECRET_KEY=fastgptsecret
INVOKE_TOKEN_SECRET=fastgpt_invoke_token_secret_32_chars_min
```
**文件**:`projects/app/.env.local`
---
## 修复后启动状态
```
✅ 4/4 Workers 编译成功
- text2Chunks
- htmlStr2Md
- countGptMessagesTokens
- readFile
✅ Next.js 16.2.6 (Turbopack) 启动成功
- Ready in 301ms
- Local: http://localhost:3000
✅ 系统初始化
- BullMQ workers 全部就绪(8 个队列)
- S3 delete worker 启动
- Collection Update worker 初始化
⚠️ Turbopack 静态分析警告
- /ROOT worker path(不影响运行时,process.cwd() 正确解析)
❌ MongoDB 连接超时
- outlinks.find() buffering timed out after 10000ms
- 原因:远程数据库 8.217.233.154:27017 不可达
```
---
## 剩余环境问题(非代码问题)
以下问题需要配置可用的外部服务才能完全启动:
| 服务 | 配置项 | 当前值 | 状态 |
|------|--------|--------|------|
| MongoDB | `MONGODB_URI` | `8.217.233.154:27017` | ❌ 不可达 |
| PostgreSQL | `PG_URL` | `8.217.233.154:5432` | ❌ 不可达 |
| MinIO/S3 | `S3_ENDPOINT` | `fastgpt-private.localhost` | ❌ 不可达 |
**解决方案**:
1. 确保远程数据库服务器可访问
2. 或修改 `.env.local` 指向可用的数据库
3. 或使用 `docker-compose.yml` 启动本地服务
---
## 修改文件清单
| # | 文件 | 操作 | 说明 |
|---|------|------|------|
| 1 | `package.json` | 修改 | packageManager 降级 + prepare 脚本修复 |
| 2 | `pnpm-workspace.yaml` | 修改 | 移除 pro/ 引用和无效 allowBuilds |
| 3 | `projects/app/.env.local` | 修改 | 添加缺失环境变量 |
| 4 | `projects/app/next.config.ts` | 修改 | 添加 proxy-agent/vm2 到 serverExternalPackages |
| 5 | `packages/service/package.json` | 修改 | 添加 OpenTelemetry 依赖 |
| 6 | `packages/service/core/app/plugin/controller.ts` | 修改 | lodash → lodash-es |
| 7 | `packages/service/core/app/tool/api.ts` | 重写 | 使用 runPluginToolStream 替代 @fastgpt-sdk/plugin |
| 8 | `packages/service/worker/tokenWorkerConfig.ts` | 新建 | 从 FastGPT-main 复制 |
| 9 | `packages/service/worker/countGptMessagesTokens/index.ts` | 替换 | FastGPT-main 版本 |
| 10 | `packages/service/worker/countGptMessagesTokens/count.ts` | 新建 | 从 FastGPT-main 复制 |
| 11 | `packages/service/worker/htmlStr2Md/index.ts` | 替换 | FastGPT-main 版本 |
| 12 | `packages/service/worker/htmlStr2Md/utils.ts` | 替换 | FastGPT-main 版本 |
| 13 | `packages/service/worker/utils/base64ImageUpload.ts` | 新建 | 从 FastGPT-main 复制 |
| 14 | `packages/service/worker/env.ts` | 新建 | 从 FastGPT-main 复制 |
| 15 | `packages/service/worker/readFile/type.d.ts` | 修改 | 新增 UploadFileHandler 类型 |
| 16 | `packages/service/worker/readFile/extension/rawText.ts` | 替换 | FastGPT-main 版本 |
| 17 | `packages/service/worker/readFile/extension/xlsx.ts` | 替换 | FastGPT-main 版本 |
| 18 | `packages/service/worker/readFile/extension/utils.ts` | 新建 | 从 FastGPT-main 复制 |
| 19 | `packages/service/worker/readFile/parseOffice.ts` | 替换 | FastGPT-main 版本 |
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment