clash-linux commited on
Commit
0c65530
·
verified ·
1 Parent(s): f0297ca

Upload 14 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ src/proxy/chrome_proxy_server_android_arm64 filter=lfs diff=lfs merge=lfs -text
37
+ src/proxy/chrome_proxy_server_linux_amd64 filter=lfs diff=lfs merge=lfs -text
38
+ src/proxy/chrome_proxy_server_windows_amd64.exe filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 依赖
2
+ node_modules/
3
+ npm-debug.log
4
+ yarn-debug.log
5
+ yarn-error.log
6
+
7
+ # 环境变量
8
+ .env
9
+ .env.local
10
+ .env.development.local
11
+ .env.test.local
12
+ .env.production.local
13
+
14
+ # 日志
15
+ logs
16
+ *.log
17
+
18
+ # 运行时数据
19
+ pids
20
+ *.pid
21
+ *.seed
22
+ *.pid.lock
23
+
24
+ # 目录
25
+ dist/
26
+ build/
27
+ coverage/
28
+
29
+ # 其他
30
+ .DS_Store
31
+ .idea/
32
+ .vscode/
33
+ *.swp
34
+ *.swo
Dockerfile ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use an official Node.js runtime as a parent image
2
+ FROM node:18-slim
3
+
4
+ # Set the working directory in the container
5
+ WORKDIR /app
6
+
7
+ # Copy package.json and package-lock.json to the working directory
8
+ COPY package*.json ./
9
+
10
+ # Install app dependencies
11
+ # Using --production flag to install only production dependencies
12
+ # Playwright needs special handling to download browsers
13
+ # We need to install dependencies first to make sure Playwright downloads its browsers.
14
+ RUN npm install --production
15
+
16
+ # Copy the rest of the application source code to the working directory
17
+ COPY . .
18
+
19
+ # Make port 7860 available to the world outside this container
20
+ EXPOSE 7860
21
+
22
+ # Define the command to run the app
23
+ CMD ["npm", "start"]
README.md CHANGED
@@ -1,11 +1,138 @@
1
- ---
2
- title: Notion
3
- emoji:
4
- colorFrom: red
5
- colorTo: pink
6
- sdk: docker
7
- pinned: false
8
- license: mit
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Notion2API Node.js
3
+ emoji: 🚀
4
+ colorFrom: green
5
+ colorTo: blue
6
+ sdk: docker
7
+ app_port: 7860
8
+ pinned: false
9
+ ---
10
+
11
+ # Notion API 轻量级客户端
12
+
13
+ 这个项目提供了一个轻量级的 Notion API 客户端,可以在资源受限的环境(如 Termux)中运行,无需完整的浏览器环境。
14
+
15
+ ## 特点
16
+
17
+ - 使用 `node-fetch` 代替 Playwright 浏览器自动化
18
+ - 轻量级设计,适合在移动设备和资源受限环境中运行
19
+ - 支持 Notion AI 的流式响应
20
+ - 兼容 OpenAI API 格式的请求和响应
21
+
22
+ ## 安装
23
+
24
+ ### 依赖项
25
+
26
+ 确保安装以下依赖:
27
+
28
+ ```bash
29
+ npm install
30
+ ```
31
+
32
+ ### 环境变量
33
+
34
+ 创建 `.env` 文件,设置以下环境变量:
35
+
36
+ ```
37
+ NOTION_COOKIE=your_notion_cookie_here
38
+ NOTION_SPACE_ID=optional_space_id
39
+ NOTION_ACTIVE_USER_HEADER=optional_user_id
40
+ PROXY_URL=optional_proxy_url
41
+ PROXY_AUTH_TOKEN=your_auth_token
42
+ PORT=7860
43
+ ```
44
+
45
+ ## 使用方法
46
+
47
+ ### 启动服务
48
+
49
+ 运行轻量级服务器:
50
+
51
+ ```bash
52
+ npm start
53
+ ```
54
+
55
+ 服务器将在指定端口(默认 7860)启动。
56
+
57
+ 如果需要使用原始的基于 Playwright 的版本(不推荐在 Termux 中使用):
58
+
59
+ ```bash
60
+ npm run original
61
+ ```
62
+
63
+ ### API 端点
64
+
65
+ - `GET /v1/models` - 获取可用模型列表
66
+ - `POST /v1/chat/completions` - 聊天完成端点
67
+ - `GET /health` - 健康检查
68
+
69
+ ### 在 Termux 中运行
70
+
71
+ 1. 安装 Termux 和 Node.js:
72
+ ```bash
73
+ pkg update
74
+ pkg install nodejs
75
+ ```
76
+
77
+ 2. 克隆项目并安装依赖:
78
+ ```bash
79
+ git clone https://github.com/yourusername/notion2api-nodejs.git
80
+ cd notion2api-nodejs
81
+ npm install
82
+ ```
83
+
84
+ 3. 设置环境变量并运行:
85
+ ```bash
86
+ npm start
87
+ ```
88
+
89
+ ## 作为模块集成
90
+
91
+ 你也可以将轻量级客户端作为模块导入到你自己的项目中:
92
+
93
+ ```javascript
94
+ import {
95
+ initialize,
96
+ streamNotionResponse,
97
+ buildNotionRequest,
98
+ FETCHED_IDS_SUCCESSFULLY
99
+ } from './lightweight-client.js';
100
+
101
+ // 初始化客户端
102
+ await initialize();
103
+
104
+ // 检查是否成功获取 Notion IDs
105
+ if (FETCHED_IDS_SUCCESSFULLY) {
106
+ // 构建请求
107
+ const requestData = {
108
+ notion_model: "openai-gpt-4.1",
109
+ messages: [
110
+ { role: "user", content: "你好,请介绍一下自己" }
111
+ ]
112
+ };
113
+
114
+ const notionRequestBody = buildNotionRequest(requestData);
115
+
116
+ // 获取响应流
117
+ const stream = await streamNotionResponse(notionRequestBody);
118
+
119
+ // 处理响应
120
+ stream.on('data', chunk => {
121
+ console.log(chunk.toString());
122
+ });
123
+ }
124
+ ```
125
+
126
+ ## 故障排除
127
+
128
+ - 如果无法获取 Notion IDs,请确保提供了有效的 NOTION_COOKIE
129
+ - 对于网络问题,可以尝试设置 PROXY_URL
130
+ - 查看日志输出以获取详细的错误信息
131
+
132
+ ## 依赖说明
133
+
134
+ - `node-fetch`: 用于发送 HTTP 请求
135
+ - `jsdom`: 提供 DOM API 的轻量级模拟
136
+ - `dotenv`: 加载环境变量
137
+ - `express`: Web 服务器框架
138
+ - `https-proxy-agent`: 支持 HTTPS 代理
package-lock.json ADDED
@@ -0,0 +1,2041 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "notion2api-nodejs",
3
+ "version": "1.0.0",
4
+ "lockfileVersion": 3,
5
+ "requires": true,
6
+ "packages": {
7
+ "": {
8
+ "name": "notion2api-nodejs",
9
+ "version": "1.0.0",
10
+ "license": "MIT",
11
+ "dependencies": {
12
+ "axios": "^1.9.0",
13
+ "chalk": "^4.1.2",
14
+ "dotenv": "^16.3.1",
15
+ "express": "^4.18.2",
16
+ "https-proxy-agent": "^7.0.2",
17
+ "jsdom": "^22.1.0",
18
+ "node-fetch": "^3.3.2",
19
+ "playwright": "^1.40.1"
20
+ },
21
+ "devDependencies": {
22
+ "nodemon": "^3.0.2"
23
+ }
24
+ },
25
+ "node_modules/@tootallnate/once": {
26
+ "version": "2.0.0",
27
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
28
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
29
+ "license": "MIT",
30
+ "engines": {
31
+ "node": ">= 10"
32
+ }
33
+ },
34
+ "node_modules/abab": {
35
+ "version": "2.0.6",
36
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
37
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
38
+ "deprecated": "Use your platform's native atob() and btoa() methods instead",
39
+ "license": "BSD-3-Clause"
40
+ },
41
+ "node_modules/accepts": {
42
+ "version": "1.3.8",
43
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
44
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
45
+ "license": "MIT",
46
+ "dependencies": {
47
+ "mime-types": "~2.1.34",
48
+ "negotiator": "0.6.3"
49
+ },
50
+ "engines": {
51
+ "node": ">= 0.6"
52
+ }
53
+ },
54
+ "node_modules/agent-base": {
55
+ "version": "7.1.3",
56
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz",
57
+ "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==",
58
+ "license": "MIT",
59
+ "engines": {
60
+ "node": ">= 14"
61
+ }
62
+ },
63
+ "node_modules/ansi-styles": {
64
+ "version": "4.3.0",
65
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
66
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
67
+ "license": "MIT",
68
+ "dependencies": {
69
+ "color-convert": "^2.0.1"
70
+ },
71
+ "engines": {
72
+ "node": ">=8"
73
+ },
74
+ "funding": {
75
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
76
+ }
77
+ },
78
+ "node_modules/anymatch": {
79
+ "version": "3.1.3",
80
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
81
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
82
+ "dev": true,
83
+ "license": "ISC",
84
+ "dependencies": {
85
+ "normalize-path": "^3.0.0",
86
+ "picomatch": "^2.0.4"
87
+ },
88
+ "engines": {
89
+ "node": ">= 8"
90
+ }
91
+ },
92
+ "node_modules/array-flatten": {
93
+ "version": "1.1.1",
94
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
95
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
96
+ "license": "MIT"
97
+ },
98
+ "node_modules/asynckit": {
99
+ "version": "0.4.0",
100
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
101
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
102
+ "license": "MIT"
103
+ },
104
+ "node_modules/axios": {
105
+ "version": "1.9.0",
106
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
107
+ "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
108
+ "license": "MIT",
109
+ "dependencies": {
110
+ "follow-redirects": "^1.15.6",
111
+ "form-data": "^4.0.0",
112
+ "proxy-from-env": "^1.1.0"
113
+ }
114
+ },
115
+ "node_modules/balanced-match": {
116
+ "version": "1.0.2",
117
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
118
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
119
+ "dev": true,
120
+ "license": "MIT"
121
+ },
122
+ "node_modules/binary-extensions": {
123
+ "version": "2.3.0",
124
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
125
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
126
+ "dev": true,
127
+ "license": "MIT",
128
+ "engines": {
129
+ "node": ">=8"
130
+ },
131
+ "funding": {
132
+ "url": "https://github.com/sponsors/sindresorhus"
133
+ }
134
+ },
135
+ "node_modules/body-parser": {
136
+ "version": "1.20.3",
137
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
138
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
139
+ "license": "MIT",
140
+ "dependencies": {
141
+ "bytes": "3.1.2",
142
+ "content-type": "~1.0.5",
143
+ "debug": "2.6.9",
144
+ "depd": "2.0.0",
145
+ "destroy": "1.2.0",
146
+ "http-errors": "2.0.0",
147
+ "iconv-lite": "0.4.24",
148
+ "on-finished": "2.4.1",
149
+ "qs": "6.13.0",
150
+ "raw-body": "2.5.2",
151
+ "type-is": "~1.6.18",
152
+ "unpipe": "1.0.0"
153
+ },
154
+ "engines": {
155
+ "node": ">= 0.8",
156
+ "npm": "1.2.8000 || >= 1.4.16"
157
+ }
158
+ },
159
+ "node_modules/brace-expansion": {
160
+ "version": "1.1.11",
161
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
162
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
163
+ "dev": true,
164
+ "license": "MIT",
165
+ "dependencies": {
166
+ "balanced-match": "^1.0.0",
167
+ "concat-map": "0.0.1"
168
+ }
169
+ },
170
+ "node_modules/braces": {
171
+ "version": "3.0.3",
172
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
173
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
174
+ "dev": true,
175
+ "license": "MIT",
176
+ "dependencies": {
177
+ "fill-range": "^7.1.1"
178
+ },
179
+ "engines": {
180
+ "node": ">=8"
181
+ }
182
+ },
183
+ "node_modules/bytes": {
184
+ "version": "3.1.2",
185
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
186
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
187
+ "license": "MIT",
188
+ "engines": {
189
+ "node": ">= 0.8"
190
+ }
191
+ },
192
+ "node_modules/call-bind-apply-helpers": {
193
+ "version": "1.0.2",
194
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
195
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
196
+ "license": "MIT",
197
+ "dependencies": {
198
+ "es-errors": "^1.3.0",
199
+ "function-bind": "^1.1.2"
200
+ },
201
+ "engines": {
202
+ "node": ">= 0.4"
203
+ }
204
+ },
205
+ "node_modules/call-bound": {
206
+ "version": "1.0.4",
207
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
208
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
209
+ "license": "MIT",
210
+ "dependencies": {
211
+ "call-bind-apply-helpers": "^1.0.2",
212
+ "get-intrinsic": "^1.3.0"
213
+ },
214
+ "engines": {
215
+ "node": ">= 0.4"
216
+ },
217
+ "funding": {
218
+ "url": "https://github.com/sponsors/ljharb"
219
+ }
220
+ },
221
+ "node_modules/chalk": {
222
+ "version": "4.1.2",
223
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
224
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
225
+ "license": "MIT",
226
+ "dependencies": {
227
+ "ansi-styles": "^4.1.0",
228
+ "supports-color": "^7.1.0"
229
+ },
230
+ "engines": {
231
+ "node": ">=10"
232
+ },
233
+ "funding": {
234
+ "url": "https://github.com/chalk/chalk?sponsor=1"
235
+ }
236
+ },
237
+ "node_modules/chalk/node_modules/has-flag": {
238
+ "version": "4.0.0",
239
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
240
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
241
+ "license": "MIT",
242
+ "engines": {
243
+ "node": ">=8"
244
+ }
245
+ },
246
+ "node_modules/chalk/node_modules/supports-color": {
247
+ "version": "7.2.0",
248
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
249
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
250
+ "license": "MIT",
251
+ "dependencies": {
252
+ "has-flag": "^4.0.0"
253
+ },
254
+ "engines": {
255
+ "node": ">=8"
256
+ }
257
+ },
258
+ "node_modules/chokidar": {
259
+ "version": "3.6.0",
260
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
261
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
262
+ "dev": true,
263
+ "license": "MIT",
264
+ "dependencies": {
265
+ "anymatch": "~3.1.2",
266
+ "braces": "~3.0.2",
267
+ "glob-parent": "~5.1.2",
268
+ "is-binary-path": "~2.1.0",
269
+ "is-glob": "~4.0.1",
270
+ "normalize-path": "~3.0.0",
271
+ "readdirp": "~3.6.0"
272
+ },
273
+ "engines": {
274
+ "node": ">= 8.10.0"
275
+ },
276
+ "funding": {
277
+ "url": "https://paulmillr.com/funding/"
278
+ },
279
+ "optionalDependencies": {
280
+ "fsevents": "~2.3.2"
281
+ }
282
+ },
283
+ "node_modules/color-convert": {
284
+ "version": "2.0.1",
285
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
286
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
287
+ "license": "MIT",
288
+ "dependencies": {
289
+ "color-name": "~1.1.4"
290
+ },
291
+ "engines": {
292
+ "node": ">=7.0.0"
293
+ }
294
+ },
295
+ "node_modules/color-name": {
296
+ "version": "1.1.4",
297
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
298
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
299
+ "license": "MIT"
300
+ },
301
+ "node_modules/combined-stream": {
302
+ "version": "1.0.8",
303
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
304
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
305
+ "license": "MIT",
306
+ "dependencies": {
307
+ "delayed-stream": "~1.0.0"
308
+ },
309
+ "engines": {
310
+ "node": ">= 0.8"
311
+ }
312
+ },
313
+ "node_modules/concat-map": {
314
+ "version": "0.0.1",
315
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
316
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
317
+ "dev": true,
318
+ "license": "MIT"
319
+ },
320
+ "node_modules/content-disposition": {
321
+ "version": "0.5.4",
322
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
323
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
324
+ "license": "MIT",
325
+ "dependencies": {
326
+ "safe-buffer": "5.2.1"
327
+ },
328
+ "engines": {
329
+ "node": ">= 0.6"
330
+ }
331
+ },
332
+ "node_modules/content-type": {
333
+ "version": "1.0.5",
334
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
335
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
336
+ "license": "MIT",
337
+ "engines": {
338
+ "node": ">= 0.6"
339
+ }
340
+ },
341
+ "node_modules/cookie": {
342
+ "version": "0.7.1",
343
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
344
+ "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
345
+ "license": "MIT",
346
+ "engines": {
347
+ "node": ">= 0.6"
348
+ }
349
+ },
350
+ "node_modules/cookie-signature": {
351
+ "version": "1.0.6",
352
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
353
+ "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
354
+ "license": "MIT"
355
+ },
356
+ "node_modules/cssstyle": {
357
+ "version": "3.0.0",
358
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
359
+ "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
360
+ "license": "MIT",
361
+ "dependencies": {
362
+ "rrweb-cssom": "^0.6.0"
363
+ },
364
+ "engines": {
365
+ "node": ">=14"
366
+ }
367
+ },
368
+ "node_modules/data-uri-to-buffer": {
369
+ "version": "4.0.1",
370
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
371
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
372
+ "license": "MIT",
373
+ "engines": {
374
+ "node": ">= 12"
375
+ }
376
+ },
377
+ "node_modules/data-urls": {
378
+ "version": "4.0.0",
379
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz",
380
+ "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==",
381
+ "license": "MIT",
382
+ "dependencies": {
383
+ "abab": "^2.0.6",
384
+ "whatwg-mimetype": "^3.0.0",
385
+ "whatwg-url": "^12.0.0"
386
+ },
387
+ "engines": {
388
+ "node": ">=14"
389
+ }
390
+ },
391
+ "node_modules/debug": {
392
+ "version": "2.6.9",
393
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
394
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
395
+ "license": "MIT",
396
+ "dependencies": {
397
+ "ms": "2.0.0"
398
+ }
399
+ },
400
+ "node_modules/decimal.js": {
401
+ "version": "10.5.0",
402
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
403
+ "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
404
+ "license": "MIT"
405
+ },
406
+ "node_modules/delayed-stream": {
407
+ "version": "1.0.0",
408
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
409
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
410
+ "license": "MIT",
411
+ "engines": {
412
+ "node": ">=0.4.0"
413
+ }
414
+ },
415
+ "node_modules/depd": {
416
+ "version": "2.0.0",
417
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
418
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
419
+ "license": "MIT",
420
+ "engines": {
421
+ "node": ">= 0.8"
422
+ }
423
+ },
424
+ "node_modules/destroy": {
425
+ "version": "1.2.0",
426
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
427
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
428
+ "license": "MIT",
429
+ "engines": {
430
+ "node": ">= 0.8",
431
+ "npm": "1.2.8000 || >= 1.4.16"
432
+ }
433
+ },
434
+ "node_modules/domexception": {
435
+ "version": "4.0.0",
436
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
437
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
438
+ "deprecated": "Use your platform's native DOMException instead",
439
+ "license": "MIT",
440
+ "dependencies": {
441
+ "webidl-conversions": "^7.0.0"
442
+ },
443
+ "engines": {
444
+ "node": ">=12"
445
+ }
446
+ },
447
+ "node_modules/dotenv": {
448
+ "version": "16.5.0",
449
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz",
450
+ "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==",
451
+ "license": "BSD-2-Clause",
452
+ "engines": {
453
+ "node": ">=12"
454
+ },
455
+ "funding": {
456
+ "url": "https://dotenvx.com"
457
+ }
458
+ },
459
+ "node_modules/dunder-proto": {
460
+ "version": "1.0.1",
461
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
462
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
463
+ "license": "MIT",
464
+ "dependencies": {
465
+ "call-bind-apply-helpers": "^1.0.1",
466
+ "es-errors": "^1.3.0",
467
+ "gopd": "^1.2.0"
468
+ },
469
+ "engines": {
470
+ "node": ">= 0.4"
471
+ }
472
+ },
473
+ "node_modules/ee-first": {
474
+ "version": "1.1.1",
475
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
476
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
477
+ "license": "MIT"
478
+ },
479
+ "node_modules/encodeurl": {
480
+ "version": "2.0.0",
481
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
482
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
483
+ "license": "MIT",
484
+ "engines": {
485
+ "node": ">= 0.8"
486
+ }
487
+ },
488
+ "node_modules/entities": {
489
+ "version": "6.0.0",
490
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz",
491
+ "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==",
492
+ "license": "BSD-2-Clause",
493
+ "engines": {
494
+ "node": ">=0.12"
495
+ },
496
+ "funding": {
497
+ "url": "https://github.com/fb55/entities?sponsor=1"
498
+ }
499
+ },
500
+ "node_modules/es-define-property": {
501
+ "version": "1.0.1",
502
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
503
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
504
+ "license": "MIT",
505
+ "engines": {
506
+ "node": ">= 0.4"
507
+ }
508
+ },
509
+ "node_modules/es-errors": {
510
+ "version": "1.3.0",
511
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
512
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
513
+ "license": "MIT",
514
+ "engines": {
515
+ "node": ">= 0.4"
516
+ }
517
+ },
518
+ "node_modules/es-object-atoms": {
519
+ "version": "1.1.1",
520
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
521
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
522
+ "license": "MIT",
523
+ "dependencies": {
524
+ "es-errors": "^1.3.0"
525
+ },
526
+ "engines": {
527
+ "node": ">= 0.4"
528
+ }
529
+ },
530
+ "node_modules/es-set-tostringtag": {
531
+ "version": "2.1.0",
532
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
533
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
534
+ "license": "MIT",
535
+ "dependencies": {
536
+ "es-errors": "^1.3.0",
537
+ "get-intrinsic": "^1.2.6",
538
+ "has-tostringtag": "^1.0.2",
539
+ "hasown": "^2.0.2"
540
+ },
541
+ "engines": {
542
+ "node": ">= 0.4"
543
+ }
544
+ },
545
+ "node_modules/escape-html": {
546
+ "version": "1.0.3",
547
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
548
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
549
+ "license": "MIT"
550
+ },
551
+ "node_modules/etag": {
552
+ "version": "1.8.1",
553
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
554
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
555
+ "license": "MIT",
556
+ "engines": {
557
+ "node": ">= 0.6"
558
+ }
559
+ },
560
+ "node_modules/express": {
561
+ "version": "4.21.2",
562
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
563
+ "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
564
+ "license": "MIT",
565
+ "dependencies": {
566
+ "accepts": "~1.3.8",
567
+ "array-flatten": "1.1.1",
568
+ "body-parser": "1.20.3",
569
+ "content-disposition": "0.5.4",
570
+ "content-type": "~1.0.4",
571
+ "cookie": "0.7.1",
572
+ "cookie-signature": "1.0.6",
573
+ "debug": "2.6.9",
574
+ "depd": "2.0.0",
575
+ "encodeurl": "~2.0.0",
576
+ "escape-html": "~1.0.3",
577
+ "etag": "~1.8.1",
578
+ "finalhandler": "1.3.1",
579
+ "fresh": "0.5.2",
580
+ "http-errors": "2.0.0",
581
+ "merge-descriptors": "1.0.3",
582
+ "methods": "~1.1.2",
583
+ "on-finished": "2.4.1",
584
+ "parseurl": "~1.3.3",
585
+ "path-to-regexp": "0.1.12",
586
+ "proxy-addr": "~2.0.7",
587
+ "qs": "6.13.0",
588
+ "range-parser": "~1.2.1",
589
+ "safe-buffer": "5.2.1",
590
+ "send": "0.19.0",
591
+ "serve-static": "1.16.2",
592
+ "setprototypeof": "1.2.0",
593
+ "statuses": "2.0.1",
594
+ "type-is": "~1.6.18",
595
+ "utils-merge": "1.0.1",
596
+ "vary": "~1.1.2"
597
+ },
598
+ "engines": {
599
+ "node": ">= 0.10.0"
600
+ },
601
+ "funding": {
602
+ "type": "opencollective",
603
+ "url": "https://opencollective.com/express"
604
+ }
605
+ },
606
+ "node_modules/fetch-blob": {
607
+ "version": "3.2.0",
608
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
609
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
610
+ "funding": [
611
+ {
612
+ "type": "github",
613
+ "url": "https://github.com/sponsors/jimmywarting"
614
+ },
615
+ {
616
+ "type": "paypal",
617
+ "url": "https://paypal.me/jimmywarting"
618
+ }
619
+ ],
620
+ "license": "MIT",
621
+ "dependencies": {
622
+ "node-domexception": "^1.0.0",
623
+ "web-streams-polyfill": "^3.0.3"
624
+ },
625
+ "engines": {
626
+ "node": "^12.20 || >= 14.13"
627
+ }
628
+ },
629
+ "node_modules/fill-range": {
630
+ "version": "7.1.1",
631
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
632
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
633
+ "dev": true,
634
+ "license": "MIT",
635
+ "dependencies": {
636
+ "to-regex-range": "^5.0.1"
637
+ },
638
+ "engines": {
639
+ "node": ">=8"
640
+ }
641
+ },
642
+ "node_modules/finalhandler": {
643
+ "version": "1.3.1",
644
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
645
+ "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
646
+ "license": "MIT",
647
+ "dependencies": {
648
+ "debug": "2.6.9",
649
+ "encodeurl": "~2.0.0",
650
+ "escape-html": "~1.0.3",
651
+ "on-finished": "2.4.1",
652
+ "parseurl": "~1.3.3",
653
+ "statuses": "2.0.1",
654
+ "unpipe": "~1.0.0"
655
+ },
656
+ "engines": {
657
+ "node": ">= 0.8"
658
+ }
659
+ },
660
+ "node_modules/follow-redirects": {
661
+ "version": "1.15.9",
662
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
663
+ "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
664
+ "funding": [
665
+ {
666
+ "type": "individual",
667
+ "url": "https://github.com/sponsors/RubenVerborgh"
668
+ }
669
+ ],
670
+ "license": "MIT",
671
+ "engines": {
672
+ "node": ">=4.0"
673
+ },
674
+ "peerDependenciesMeta": {
675
+ "debug": {
676
+ "optional": true
677
+ }
678
+ }
679
+ },
680
+ "node_modules/form-data": {
681
+ "version": "4.0.2",
682
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
683
+ "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
684
+ "license": "MIT",
685
+ "dependencies": {
686
+ "asynckit": "^0.4.0",
687
+ "combined-stream": "^1.0.8",
688
+ "es-set-tostringtag": "^2.1.0",
689
+ "mime-types": "^2.1.12"
690
+ },
691
+ "engines": {
692
+ "node": ">= 6"
693
+ }
694
+ },
695
+ "node_modules/formdata-polyfill": {
696
+ "version": "4.0.10",
697
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
698
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
699
+ "license": "MIT",
700
+ "dependencies": {
701
+ "fetch-blob": "^3.1.2"
702
+ },
703
+ "engines": {
704
+ "node": ">=12.20.0"
705
+ }
706
+ },
707
+ "node_modules/forwarded": {
708
+ "version": "0.2.0",
709
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
710
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
711
+ "license": "MIT",
712
+ "engines": {
713
+ "node": ">= 0.6"
714
+ }
715
+ },
716
+ "node_modules/fresh": {
717
+ "version": "0.5.2",
718
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
719
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
720
+ "license": "MIT",
721
+ "engines": {
722
+ "node": ">= 0.6"
723
+ }
724
+ },
725
+ "node_modules/fsevents": {
726
+ "version": "2.3.3",
727
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
728
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
729
+ "dev": true,
730
+ "hasInstallScript": true,
731
+ "license": "MIT",
732
+ "optional": true,
733
+ "os": [
734
+ "darwin"
735
+ ],
736
+ "engines": {
737
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
738
+ }
739
+ },
740
+ "node_modules/function-bind": {
741
+ "version": "1.1.2",
742
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
743
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
744
+ "license": "MIT",
745
+ "funding": {
746
+ "url": "https://github.com/sponsors/ljharb"
747
+ }
748
+ },
749
+ "node_modules/get-intrinsic": {
750
+ "version": "1.3.0",
751
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
752
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
753
+ "license": "MIT",
754
+ "dependencies": {
755
+ "call-bind-apply-helpers": "^1.0.2",
756
+ "es-define-property": "^1.0.1",
757
+ "es-errors": "^1.3.0",
758
+ "es-object-atoms": "^1.1.1",
759
+ "function-bind": "^1.1.2",
760
+ "get-proto": "^1.0.1",
761
+ "gopd": "^1.2.0",
762
+ "has-symbols": "^1.1.0",
763
+ "hasown": "^2.0.2",
764
+ "math-intrinsics": "^1.1.0"
765
+ },
766
+ "engines": {
767
+ "node": ">= 0.4"
768
+ },
769
+ "funding": {
770
+ "url": "https://github.com/sponsors/ljharb"
771
+ }
772
+ },
773
+ "node_modules/get-proto": {
774
+ "version": "1.0.1",
775
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
776
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
777
+ "license": "MIT",
778
+ "dependencies": {
779
+ "dunder-proto": "^1.0.1",
780
+ "es-object-atoms": "^1.0.0"
781
+ },
782
+ "engines": {
783
+ "node": ">= 0.4"
784
+ }
785
+ },
786
+ "node_modules/glob-parent": {
787
+ "version": "5.1.2",
788
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
789
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
790
+ "dev": true,
791
+ "license": "ISC",
792
+ "dependencies": {
793
+ "is-glob": "^4.0.1"
794
+ },
795
+ "engines": {
796
+ "node": ">= 6"
797
+ }
798
+ },
799
+ "node_modules/gopd": {
800
+ "version": "1.2.0",
801
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
802
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
803
+ "license": "MIT",
804
+ "engines": {
805
+ "node": ">= 0.4"
806
+ },
807
+ "funding": {
808
+ "url": "https://github.com/sponsors/ljharb"
809
+ }
810
+ },
811
+ "node_modules/has-flag": {
812
+ "version": "3.0.0",
813
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
814
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
815
+ "dev": true,
816
+ "license": "MIT",
817
+ "engines": {
818
+ "node": ">=4"
819
+ }
820
+ },
821
+ "node_modules/has-symbols": {
822
+ "version": "1.1.0",
823
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
824
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
825
+ "license": "MIT",
826
+ "engines": {
827
+ "node": ">= 0.4"
828
+ },
829
+ "funding": {
830
+ "url": "https://github.com/sponsors/ljharb"
831
+ }
832
+ },
833
+ "node_modules/has-tostringtag": {
834
+ "version": "1.0.2",
835
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
836
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
837
+ "license": "MIT",
838
+ "dependencies": {
839
+ "has-symbols": "^1.0.3"
840
+ },
841
+ "engines": {
842
+ "node": ">= 0.4"
843
+ },
844
+ "funding": {
845
+ "url": "https://github.com/sponsors/ljharb"
846
+ }
847
+ },
848
+ "node_modules/hasown": {
849
+ "version": "2.0.2",
850
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
851
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
852
+ "license": "MIT",
853
+ "dependencies": {
854
+ "function-bind": "^1.1.2"
855
+ },
856
+ "engines": {
857
+ "node": ">= 0.4"
858
+ }
859
+ },
860
+ "node_modules/html-encoding-sniffer": {
861
+ "version": "3.0.0",
862
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
863
+ "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
864
+ "license": "MIT",
865
+ "dependencies": {
866
+ "whatwg-encoding": "^2.0.0"
867
+ },
868
+ "engines": {
869
+ "node": ">=12"
870
+ }
871
+ },
872
+ "node_modules/http-errors": {
873
+ "version": "2.0.0",
874
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
875
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
876
+ "license": "MIT",
877
+ "dependencies": {
878
+ "depd": "2.0.0",
879
+ "inherits": "2.0.4",
880
+ "setprototypeof": "1.2.0",
881
+ "statuses": "2.0.1",
882
+ "toidentifier": "1.0.1"
883
+ },
884
+ "engines": {
885
+ "node": ">= 0.8"
886
+ }
887
+ },
888
+ "node_modules/http-proxy-agent": {
889
+ "version": "5.0.0",
890
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
891
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
892
+ "license": "MIT",
893
+ "dependencies": {
894
+ "@tootallnate/once": "2",
895
+ "agent-base": "6",
896
+ "debug": "4"
897
+ },
898
+ "engines": {
899
+ "node": ">= 6"
900
+ }
901
+ },
902
+ "node_modules/http-proxy-agent/node_modules/agent-base": {
903
+ "version": "6.0.2",
904
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
905
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
906
+ "license": "MIT",
907
+ "dependencies": {
908
+ "debug": "4"
909
+ },
910
+ "engines": {
911
+ "node": ">= 6.0.0"
912
+ }
913
+ },
914
+ "node_modules/http-proxy-agent/node_modules/debug": {
915
+ "version": "4.4.1",
916
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
917
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
918
+ "license": "MIT",
919
+ "dependencies": {
920
+ "ms": "^2.1.3"
921
+ },
922
+ "engines": {
923
+ "node": ">=6.0"
924
+ },
925
+ "peerDependenciesMeta": {
926
+ "supports-color": {
927
+ "optional": true
928
+ }
929
+ }
930
+ },
931
+ "node_modules/http-proxy-agent/node_modules/ms": {
932
+ "version": "2.1.3",
933
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
934
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
935
+ "license": "MIT"
936
+ },
937
+ "node_modules/https-proxy-agent": {
938
+ "version": "7.0.6",
939
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
940
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
941
+ "license": "MIT",
942
+ "dependencies": {
943
+ "agent-base": "^7.1.2",
944
+ "debug": "4"
945
+ },
946
+ "engines": {
947
+ "node": ">= 14"
948
+ }
949
+ },
950
+ "node_modules/https-proxy-agent/node_modules/debug": {
951
+ "version": "4.4.1",
952
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
953
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
954
+ "license": "MIT",
955
+ "dependencies": {
956
+ "ms": "^2.1.3"
957
+ },
958
+ "engines": {
959
+ "node": ">=6.0"
960
+ },
961
+ "peerDependenciesMeta": {
962
+ "supports-color": {
963
+ "optional": true
964
+ }
965
+ }
966
+ },
967
+ "node_modules/https-proxy-agent/node_modules/ms": {
968
+ "version": "2.1.3",
969
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
970
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
971
+ "license": "MIT"
972
+ },
973
+ "node_modules/iconv-lite": {
974
+ "version": "0.4.24",
975
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
976
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
977
+ "license": "MIT",
978
+ "dependencies": {
979
+ "safer-buffer": ">= 2.1.2 < 3"
980
+ },
981
+ "engines": {
982
+ "node": ">=0.10.0"
983
+ }
984
+ },
985
+ "node_modules/ignore-by-default": {
986
+ "version": "1.0.1",
987
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
988
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
989
+ "dev": true,
990
+ "license": "ISC"
991
+ },
992
+ "node_modules/inherits": {
993
+ "version": "2.0.4",
994
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
995
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
996
+ "license": "ISC"
997
+ },
998
+ "node_modules/ipaddr.js": {
999
+ "version": "1.9.1",
1000
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1001
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
1002
+ "license": "MIT",
1003
+ "engines": {
1004
+ "node": ">= 0.10"
1005
+ }
1006
+ },
1007
+ "node_modules/is-binary-path": {
1008
+ "version": "2.1.0",
1009
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
1010
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
1011
+ "dev": true,
1012
+ "license": "MIT",
1013
+ "dependencies": {
1014
+ "binary-extensions": "^2.0.0"
1015
+ },
1016
+ "engines": {
1017
+ "node": ">=8"
1018
+ }
1019
+ },
1020
+ "node_modules/is-extglob": {
1021
+ "version": "2.1.1",
1022
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
1023
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
1024
+ "dev": true,
1025
+ "license": "MIT",
1026
+ "engines": {
1027
+ "node": ">=0.10.0"
1028
+ }
1029
+ },
1030
+ "node_modules/is-glob": {
1031
+ "version": "4.0.3",
1032
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
1033
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
1034
+ "dev": true,
1035
+ "license": "MIT",
1036
+ "dependencies": {
1037
+ "is-extglob": "^2.1.1"
1038
+ },
1039
+ "engines": {
1040
+ "node": ">=0.10.0"
1041
+ }
1042
+ },
1043
+ "node_modules/is-number": {
1044
+ "version": "7.0.0",
1045
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
1046
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
1047
+ "dev": true,
1048
+ "license": "MIT",
1049
+ "engines": {
1050
+ "node": ">=0.12.0"
1051
+ }
1052
+ },
1053
+ "node_modules/is-potential-custom-element-name": {
1054
+ "version": "1.0.1",
1055
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
1056
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
1057
+ "license": "MIT"
1058
+ },
1059
+ "node_modules/jsdom": {
1060
+ "version": "22.1.0",
1061
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.1.0.tgz",
1062
+ "integrity": "sha512-/9AVW7xNbsBv6GfWho4TTNjEo9fe6Zhf9O7s0Fhhr3u+awPwAJMKwAMXnkk5vBxflqLW9hTHX/0cs+P3gW+cQw==",
1063
+ "license": "MIT",
1064
+ "dependencies": {
1065
+ "abab": "^2.0.6",
1066
+ "cssstyle": "^3.0.0",
1067
+ "data-urls": "^4.0.0",
1068
+ "decimal.js": "^10.4.3",
1069
+ "domexception": "^4.0.0",
1070
+ "form-data": "^4.0.0",
1071
+ "html-encoding-sniffer": "^3.0.0",
1072
+ "http-proxy-agent": "^5.0.0",
1073
+ "https-proxy-agent": "^5.0.1",
1074
+ "is-potential-custom-element-name": "^1.0.1",
1075
+ "nwsapi": "^2.2.4",
1076
+ "parse5": "^7.1.2",
1077
+ "rrweb-cssom": "^0.6.0",
1078
+ "saxes": "^6.0.0",
1079
+ "symbol-tree": "^3.2.4",
1080
+ "tough-cookie": "^4.1.2",
1081
+ "w3c-xmlserializer": "^4.0.0",
1082
+ "webidl-conversions": "^7.0.0",
1083
+ "whatwg-encoding": "^2.0.0",
1084
+ "whatwg-mimetype": "^3.0.0",
1085
+ "whatwg-url": "^12.0.1",
1086
+ "ws": "^8.13.0",
1087
+ "xml-name-validator": "^4.0.0"
1088
+ },
1089
+ "engines": {
1090
+ "node": ">=16"
1091
+ },
1092
+ "peerDependencies": {
1093
+ "canvas": "^2.5.0"
1094
+ },
1095
+ "peerDependenciesMeta": {
1096
+ "canvas": {
1097
+ "optional": true
1098
+ }
1099
+ }
1100
+ },
1101
+ "node_modules/jsdom/node_modules/agent-base": {
1102
+ "version": "6.0.2",
1103
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
1104
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
1105
+ "license": "MIT",
1106
+ "dependencies": {
1107
+ "debug": "4"
1108
+ },
1109
+ "engines": {
1110
+ "node": ">= 6.0.0"
1111
+ }
1112
+ },
1113
+ "node_modules/jsdom/node_modules/debug": {
1114
+ "version": "4.4.1",
1115
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
1116
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
1117
+ "license": "MIT",
1118
+ "dependencies": {
1119
+ "ms": "^2.1.3"
1120
+ },
1121
+ "engines": {
1122
+ "node": ">=6.0"
1123
+ },
1124
+ "peerDependenciesMeta": {
1125
+ "supports-color": {
1126
+ "optional": true
1127
+ }
1128
+ }
1129
+ },
1130
+ "node_modules/jsdom/node_modules/https-proxy-agent": {
1131
+ "version": "5.0.1",
1132
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
1133
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
1134
+ "license": "MIT",
1135
+ "dependencies": {
1136
+ "agent-base": "6",
1137
+ "debug": "4"
1138
+ },
1139
+ "engines": {
1140
+ "node": ">= 6"
1141
+ }
1142
+ },
1143
+ "node_modules/jsdom/node_modules/ms": {
1144
+ "version": "2.1.3",
1145
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1146
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1147
+ "license": "MIT"
1148
+ },
1149
+ "node_modules/math-intrinsics": {
1150
+ "version": "1.1.0",
1151
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
1152
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
1153
+ "license": "MIT",
1154
+ "engines": {
1155
+ "node": ">= 0.4"
1156
+ }
1157
+ },
1158
+ "node_modules/media-typer": {
1159
+ "version": "0.3.0",
1160
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1161
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1162
+ "license": "MIT",
1163
+ "engines": {
1164
+ "node": ">= 0.6"
1165
+ }
1166
+ },
1167
+ "node_modules/merge-descriptors": {
1168
+ "version": "1.0.3",
1169
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
1170
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
1171
+ "license": "MIT",
1172
+ "funding": {
1173
+ "url": "https://github.com/sponsors/sindresorhus"
1174
+ }
1175
+ },
1176
+ "node_modules/methods": {
1177
+ "version": "1.1.2",
1178
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1179
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
1180
+ "license": "MIT",
1181
+ "engines": {
1182
+ "node": ">= 0.6"
1183
+ }
1184
+ },
1185
+ "node_modules/mime": {
1186
+ "version": "1.6.0",
1187
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1188
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1189
+ "license": "MIT",
1190
+ "bin": {
1191
+ "mime": "cli.js"
1192
+ },
1193
+ "engines": {
1194
+ "node": ">=4"
1195
+ }
1196
+ },
1197
+ "node_modules/mime-db": {
1198
+ "version": "1.52.0",
1199
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1200
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1201
+ "license": "MIT",
1202
+ "engines": {
1203
+ "node": ">= 0.6"
1204
+ }
1205
+ },
1206
+ "node_modules/mime-types": {
1207
+ "version": "2.1.35",
1208
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1209
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1210
+ "license": "MIT",
1211
+ "dependencies": {
1212
+ "mime-db": "1.52.0"
1213
+ },
1214
+ "engines": {
1215
+ "node": ">= 0.6"
1216
+ }
1217
+ },
1218
+ "node_modules/minimatch": {
1219
+ "version": "3.1.2",
1220
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1221
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1222
+ "dev": true,
1223
+ "license": "ISC",
1224
+ "dependencies": {
1225
+ "brace-expansion": "^1.1.7"
1226
+ },
1227
+ "engines": {
1228
+ "node": "*"
1229
+ }
1230
+ },
1231
+ "node_modules/ms": {
1232
+ "version": "2.0.0",
1233
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1234
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1235
+ "license": "MIT"
1236
+ },
1237
+ "node_modules/negotiator": {
1238
+ "version": "0.6.3",
1239
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1240
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1241
+ "license": "MIT",
1242
+ "engines": {
1243
+ "node": ">= 0.6"
1244
+ }
1245
+ },
1246
+ "node_modules/node-domexception": {
1247
+ "version": "1.0.0",
1248
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
1249
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
1250
+ "deprecated": "Use your platform's native DOMException instead",
1251
+ "funding": [
1252
+ {
1253
+ "type": "github",
1254
+ "url": "https://github.com/sponsors/jimmywarting"
1255
+ },
1256
+ {
1257
+ "type": "github",
1258
+ "url": "https://paypal.me/jimmywarting"
1259
+ }
1260
+ ],
1261
+ "license": "MIT",
1262
+ "engines": {
1263
+ "node": ">=10.5.0"
1264
+ }
1265
+ },
1266
+ "node_modules/node-fetch": {
1267
+ "version": "3.3.2",
1268
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
1269
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
1270
+ "license": "MIT",
1271
+ "dependencies": {
1272
+ "data-uri-to-buffer": "^4.0.0",
1273
+ "fetch-blob": "^3.1.4",
1274
+ "formdata-polyfill": "^4.0.10"
1275
+ },
1276
+ "engines": {
1277
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
1278
+ },
1279
+ "funding": {
1280
+ "type": "opencollective",
1281
+ "url": "https://opencollective.com/node-fetch"
1282
+ }
1283
+ },
1284
+ "node_modules/nodemon": {
1285
+ "version": "3.1.10",
1286
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
1287
+ "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
1288
+ "dev": true,
1289
+ "license": "MIT",
1290
+ "dependencies": {
1291
+ "chokidar": "^3.5.2",
1292
+ "debug": "^4",
1293
+ "ignore-by-default": "^1.0.1",
1294
+ "minimatch": "^3.1.2",
1295
+ "pstree.remy": "^1.1.8",
1296
+ "semver": "^7.5.3",
1297
+ "simple-update-notifier": "^2.0.0",
1298
+ "supports-color": "^5.5.0",
1299
+ "touch": "^3.1.0",
1300
+ "undefsafe": "^2.0.5"
1301
+ },
1302
+ "bin": {
1303
+ "nodemon": "bin/nodemon.js"
1304
+ },
1305
+ "engines": {
1306
+ "node": ">=10"
1307
+ },
1308
+ "funding": {
1309
+ "type": "opencollective",
1310
+ "url": "https://opencollective.com/nodemon"
1311
+ }
1312
+ },
1313
+ "node_modules/nodemon/node_modules/debug": {
1314
+ "version": "4.4.1",
1315
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
1316
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
1317
+ "dev": true,
1318
+ "license": "MIT",
1319
+ "dependencies": {
1320
+ "ms": "^2.1.3"
1321
+ },
1322
+ "engines": {
1323
+ "node": ">=6.0"
1324
+ },
1325
+ "peerDependenciesMeta": {
1326
+ "supports-color": {
1327
+ "optional": true
1328
+ }
1329
+ }
1330
+ },
1331
+ "node_modules/nodemon/node_modules/ms": {
1332
+ "version": "2.1.3",
1333
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1334
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1335
+ "dev": true,
1336
+ "license": "MIT"
1337
+ },
1338
+ "node_modules/normalize-path": {
1339
+ "version": "3.0.0",
1340
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1341
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1342
+ "dev": true,
1343
+ "license": "MIT",
1344
+ "engines": {
1345
+ "node": ">=0.10.0"
1346
+ }
1347
+ },
1348
+ "node_modules/nwsapi": {
1349
+ "version": "2.2.20",
1350
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz",
1351
+ "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==",
1352
+ "license": "MIT"
1353
+ },
1354
+ "node_modules/object-inspect": {
1355
+ "version": "1.13.4",
1356
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
1357
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
1358
+ "license": "MIT",
1359
+ "engines": {
1360
+ "node": ">= 0.4"
1361
+ },
1362
+ "funding": {
1363
+ "url": "https://github.com/sponsors/ljharb"
1364
+ }
1365
+ },
1366
+ "node_modules/on-finished": {
1367
+ "version": "2.4.1",
1368
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1369
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1370
+ "license": "MIT",
1371
+ "dependencies": {
1372
+ "ee-first": "1.1.1"
1373
+ },
1374
+ "engines": {
1375
+ "node": ">= 0.8"
1376
+ }
1377
+ },
1378
+ "node_modules/parse5": {
1379
+ "version": "7.3.0",
1380
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
1381
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
1382
+ "license": "MIT",
1383
+ "dependencies": {
1384
+ "entities": "^6.0.0"
1385
+ },
1386
+ "funding": {
1387
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
1388
+ }
1389
+ },
1390
+ "node_modules/parseurl": {
1391
+ "version": "1.3.3",
1392
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1393
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1394
+ "license": "MIT",
1395
+ "engines": {
1396
+ "node": ">= 0.8"
1397
+ }
1398
+ },
1399
+ "node_modules/path-to-regexp": {
1400
+ "version": "0.1.12",
1401
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
1402
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
1403
+ "license": "MIT"
1404
+ },
1405
+ "node_modules/picomatch": {
1406
+ "version": "2.3.1",
1407
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1408
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1409
+ "dev": true,
1410
+ "license": "MIT",
1411
+ "engines": {
1412
+ "node": ">=8.6"
1413
+ },
1414
+ "funding": {
1415
+ "url": "https://github.com/sponsors/jonschlinkert"
1416
+ }
1417
+ },
1418
+ "node_modules/playwright": {
1419
+ "version": "1.52.0",
1420
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz",
1421
+ "integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==",
1422
+ "license": "Apache-2.0",
1423
+ "dependencies": {
1424
+ "playwright-core": "1.52.0"
1425
+ },
1426
+ "bin": {
1427
+ "playwright": "cli.js"
1428
+ },
1429
+ "engines": {
1430
+ "node": ">=18"
1431
+ },
1432
+ "optionalDependencies": {
1433
+ "fsevents": "2.3.2"
1434
+ }
1435
+ },
1436
+ "node_modules/playwright-core": {
1437
+ "version": "1.52.0",
1438
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz",
1439
+ "integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==",
1440
+ "license": "Apache-2.0",
1441
+ "bin": {
1442
+ "playwright-core": "cli.js"
1443
+ },
1444
+ "engines": {
1445
+ "node": ">=18"
1446
+ }
1447
+ },
1448
+ "node_modules/playwright/node_modules/fsevents": {
1449
+ "version": "2.3.2",
1450
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
1451
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
1452
+ "hasInstallScript": true,
1453
+ "license": "MIT",
1454
+ "optional": true,
1455
+ "os": [
1456
+ "darwin"
1457
+ ],
1458
+ "engines": {
1459
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
1460
+ }
1461
+ },
1462
+ "node_modules/proxy-addr": {
1463
+ "version": "2.0.7",
1464
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1465
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1466
+ "license": "MIT",
1467
+ "dependencies": {
1468
+ "forwarded": "0.2.0",
1469
+ "ipaddr.js": "1.9.1"
1470
+ },
1471
+ "engines": {
1472
+ "node": ">= 0.10"
1473
+ }
1474
+ },
1475
+ "node_modules/proxy-from-env": {
1476
+ "version": "1.1.0",
1477
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
1478
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
1479
+ "license": "MIT"
1480
+ },
1481
+ "node_modules/psl": {
1482
+ "version": "1.15.0",
1483
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
1484
+ "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
1485
+ "license": "MIT",
1486
+ "dependencies": {
1487
+ "punycode": "^2.3.1"
1488
+ },
1489
+ "funding": {
1490
+ "url": "https://github.com/sponsors/lupomontero"
1491
+ }
1492
+ },
1493
+ "node_modules/pstree.remy": {
1494
+ "version": "1.1.8",
1495
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1496
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
1497
+ "dev": true,
1498
+ "license": "MIT"
1499
+ },
1500
+ "node_modules/punycode": {
1501
+ "version": "2.3.1",
1502
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
1503
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
1504
+ "license": "MIT",
1505
+ "engines": {
1506
+ "node": ">=6"
1507
+ }
1508
+ },
1509
+ "node_modules/qs": {
1510
+ "version": "6.13.0",
1511
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
1512
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
1513
+ "license": "BSD-3-Clause",
1514
+ "dependencies": {
1515
+ "side-channel": "^1.0.6"
1516
+ },
1517
+ "engines": {
1518
+ "node": ">=0.6"
1519
+ },
1520
+ "funding": {
1521
+ "url": "https://github.com/sponsors/ljharb"
1522
+ }
1523
+ },
1524
+ "node_modules/querystringify": {
1525
+ "version": "2.2.0",
1526
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
1527
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
1528
+ "license": "MIT"
1529
+ },
1530
+ "node_modules/range-parser": {
1531
+ "version": "1.2.1",
1532
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1533
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1534
+ "license": "MIT",
1535
+ "engines": {
1536
+ "node": ">= 0.6"
1537
+ }
1538
+ },
1539
+ "node_modules/raw-body": {
1540
+ "version": "2.5.2",
1541
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
1542
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
1543
+ "license": "MIT",
1544
+ "dependencies": {
1545
+ "bytes": "3.1.2",
1546
+ "http-errors": "2.0.0",
1547
+ "iconv-lite": "0.4.24",
1548
+ "unpipe": "1.0.0"
1549
+ },
1550
+ "engines": {
1551
+ "node": ">= 0.8"
1552
+ }
1553
+ },
1554
+ "node_modules/readdirp": {
1555
+ "version": "3.6.0",
1556
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1557
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1558
+ "dev": true,
1559
+ "license": "MIT",
1560
+ "dependencies": {
1561
+ "picomatch": "^2.2.1"
1562
+ },
1563
+ "engines": {
1564
+ "node": ">=8.10.0"
1565
+ }
1566
+ },
1567
+ "node_modules/requires-port": {
1568
+ "version": "1.0.0",
1569
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
1570
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
1571
+ "license": "MIT"
1572
+ },
1573
+ "node_modules/rrweb-cssom": {
1574
+ "version": "0.6.0",
1575
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
1576
+ "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==",
1577
+ "license": "MIT"
1578
+ },
1579
+ "node_modules/safe-buffer": {
1580
+ "version": "5.2.1",
1581
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1582
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1583
+ "funding": [
1584
+ {
1585
+ "type": "github",
1586
+ "url": "https://github.com/sponsors/feross"
1587
+ },
1588
+ {
1589
+ "type": "patreon",
1590
+ "url": "https://www.patreon.com/feross"
1591
+ },
1592
+ {
1593
+ "type": "consulting",
1594
+ "url": "https://feross.org/support"
1595
+ }
1596
+ ],
1597
+ "license": "MIT"
1598
+ },
1599
+ "node_modules/safer-buffer": {
1600
+ "version": "2.1.2",
1601
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1602
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1603
+ "license": "MIT"
1604
+ },
1605
+ "node_modules/saxes": {
1606
+ "version": "6.0.0",
1607
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
1608
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
1609
+ "license": "ISC",
1610
+ "dependencies": {
1611
+ "xmlchars": "^2.2.0"
1612
+ },
1613
+ "engines": {
1614
+ "node": ">=v12.22.7"
1615
+ }
1616
+ },
1617
+ "node_modules/semver": {
1618
+ "version": "7.7.2",
1619
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
1620
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
1621
+ "dev": true,
1622
+ "license": "ISC",
1623
+ "bin": {
1624
+ "semver": "bin/semver.js"
1625
+ },
1626
+ "engines": {
1627
+ "node": ">=10"
1628
+ }
1629
+ },
1630
+ "node_modules/send": {
1631
+ "version": "0.19.0",
1632
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
1633
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
1634
+ "license": "MIT",
1635
+ "dependencies": {
1636
+ "debug": "2.6.9",
1637
+ "depd": "2.0.0",
1638
+ "destroy": "1.2.0",
1639
+ "encodeurl": "~1.0.2",
1640
+ "escape-html": "~1.0.3",
1641
+ "etag": "~1.8.1",
1642
+ "fresh": "0.5.2",
1643
+ "http-errors": "2.0.0",
1644
+ "mime": "1.6.0",
1645
+ "ms": "2.1.3",
1646
+ "on-finished": "2.4.1",
1647
+ "range-parser": "~1.2.1",
1648
+ "statuses": "2.0.1"
1649
+ },
1650
+ "engines": {
1651
+ "node": ">= 0.8.0"
1652
+ }
1653
+ },
1654
+ "node_modules/send/node_modules/encodeurl": {
1655
+ "version": "1.0.2",
1656
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
1657
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
1658
+ "license": "MIT",
1659
+ "engines": {
1660
+ "node": ">= 0.8"
1661
+ }
1662
+ },
1663
+ "node_modules/send/node_modules/ms": {
1664
+ "version": "2.1.3",
1665
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1666
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1667
+ "license": "MIT"
1668
+ },
1669
+ "node_modules/serve-static": {
1670
+ "version": "1.16.2",
1671
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
1672
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
1673
+ "license": "MIT",
1674
+ "dependencies": {
1675
+ "encodeurl": "~2.0.0",
1676
+ "escape-html": "~1.0.3",
1677
+ "parseurl": "~1.3.3",
1678
+ "send": "0.19.0"
1679
+ },
1680
+ "engines": {
1681
+ "node": ">= 0.8.0"
1682
+ }
1683
+ },
1684
+ "node_modules/setprototypeof": {
1685
+ "version": "1.2.0",
1686
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1687
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1688
+ "license": "ISC"
1689
+ },
1690
+ "node_modules/side-channel": {
1691
+ "version": "1.1.0",
1692
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1693
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1694
+ "license": "MIT",
1695
+ "dependencies": {
1696
+ "es-errors": "^1.3.0",
1697
+ "object-inspect": "^1.13.3",
1698
+ "side-channel-list": "^1.0.0",
1699
+ "side-channel-map": "^1.0.1",
1700
+ "side-channel-weakmap": "^1.0.2"
1701
+ },
1702
+ "engines": {
1703
+ "node": ">= 0.4"
1704
+ },
1705
+ "funding": {
1706
+ "url": "https://github.com/sponsors/ljharb"
1707
+ }
1708
+ },
1709
+ "node_modules/side-channel-list": {
1710
+ "version": "1.0.0",
1711
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1712
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1713
+ "license": "MIT",
1714
+ "dependencies": {
1715
+ "es-errors": "^1.3.0",
1716
+ "object-inspect": "^1.13.3"
1717
+ },
1718
+ "engines": {
1719
+ "node": ">= 0.4"
1720
+ },
1721
+ "funding": {
1722
+ "url": "https://github.com/sponsors/ljharb"
1723
+ }
1724
+ },
1725
+ "node_modules/side-channel-map": {
1726
+ "version": "1.0.1",
1727
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1728
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1729
+ "license": "MIT",
1730
+ "dependencies": {
1731
+ "call-bound": "^1.0.2",
1732
+ "es-errors": "^1.3.0",
1733
+ "get-intrinsic": "^1.2.5",
1734
+ "object-inspect": "^1.13.3"
1735
+ },
1736
+ "engines": {
1737
+ "node": ">= 0.4"
1738
+ },
1739
+ "funding": {
1740
+ "url": "https://github.com/sponsors/ljharb"
1741
+ }
1742
+ },
1743
+ "node_modules/side-channel-weakmap": {
1744
+ "version": "1.0.2",
1745
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1746
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1747
+ "license": "MIT",
1748
+ "dependencies": {
1749
+ "call-bound": "^1.0.2",
1750
+ "es-errors": "^1.3.0",
1751
+ "get-intrinsic": "^1.2.5",
1752
+ "object-inspect": "^1.13.3",
1753
+ "side-channel-map": "^1.0.1"
1754
+ },
1755
+ "engines": {
1756
+ "node": ">= 0.4"
1757
+ },
1758
+ "funding": {
1759
+ "url": "https://github.com/sponsors/ljharb"
1760
+ }
1761
+ },
1762
+ "node_modules/simple-update-notifier": {
1763
+ "version": "2.0.0",
1764
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1765
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1766
+ "dev": true,
1767
+ "license": "MIT",
1768
+ "dependencies": {
1769
+ "semver": "^7.5.3"
1770
+ },
1771
+ "engines": {
1772
+ "node": ">=10"
1773
+ }
1774
+ },
1775
+ "node_modules/statuses": {
1776
+ "version": "2.0.1",
1777
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1778
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1779
+ "license": "MIT",
1780
+ "engines": {
1781
+ "node": ">= 0.8"
1782
+ }
1783
+ },
1784
+ "node_modules/supports-color": {
1785
+ "version": "5.5.0",
1786
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1787
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1788
+ "dev": true,
1789
+ "license": "MIT",
1790
+ "dependencies": {
1791
+ "has-flag": "^3.0.0"
1792
+ },
1793
+ "engines": {
1794
+ "node": ">=4"
1795
+ }
1796
+ },
1797
+ "node_modules/symbol-tree": {
1798
+ "version": "3.2.4",
1799
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
1800
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
1801
+ "license": "MIT"
1802
+ },
1803
+ "node_modules/to-regex-range": {
1804
+ "version": "5.0.1",
1805
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1806
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1807
+ "dev": true,
1808
+ "license": "MIT",
1809
+ "dependencies": {
1810
+ "is-number": "^7.0.0"
1811
+ },
1812
+ "engines": {
1813
+ "node": ">=8.0"
1814
+ }
1815
+ },
1816
+ "node_modules/toidentifier": {
1817
+ "version": "1.0.1",
1818
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1819
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1820
+ "license": "MIT",
1821
+ "engines": {
1822
+ "node": ">=0.6"
1823
+ }
1824
+ },
1825
+ "node_modules/touch": {
1826
+ "version": "3.1.1",
1827
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
1828
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
1829
+ "dev": true,
1830
+ "license": "ISC",
1831
+ "bin": {
1832
+ "nodetouch": "bin/nodetouch.js"
1833
+ }
1834
+ },
1835
+ "node_modules/tough-cookie": {
1836
+ "version": "4.1.4",
1837
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
1838
+ "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==",
1839
+ "license": "BSD-3-Clause",
1840
+ "dependencies": {
1841
+ "psl": "^1.1.33",
1842
+ "punycode": "^2.1.1",
1843
+ "universalify": "^0.2.0",
1844
+ "url-parse": "^1.5.3"
1845
+ },
1846
+ "engines": {
1847
+ "node": ">=6"
1848
+ }
1849
+ },
1850
+ "node_modules/tr46": {
1851
+ "version": "4.1.1",
1852
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
1853
+ "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
1854
+ "license": "MIT",
1855
+ "dependencies": {
1856
+ "punycode": "^2.3.0"
1857
+ },
1858
+ "engines": {
1859
+ "node": ">=14"
1860
+ }
1861
+ },
1862
+ "node_modules/type-is": {
1863
+ "version": "1.6.18",
1864
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1865
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1866
+ "license": "MIT",
1867
+ "dependencies": {
1868
+ "media-typer": "0.3.0",
1869
+ "mime-types": "~2.1.24"
1870
+ },
1871
+ "engines": {
1872
+ "node": ">= 0.6"
1873
+ }
1874
+ },
1875
+ "node_modules/undefsafe": {
1876
+ "version": "2.0.5",
1877
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1878
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
1879
+ "dev": true,
1880
+ "license": "MIT"
1881
+ },
1882
+ "node_modules/universalify": {
1883
+ "version": "0.2.0",
1884
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
1885
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
1886
+ "license": "MIT",
1887
+ "engines": {
1888
+ "node": ">= 4.0.0"
1889
+ }
1890
+ },
1891
+ "node_modules/unpipe": {
1892
+ "version": "1.0.0",
1893
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1894
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1895
+ "license": "MIT",
1896
+ "engines": {
1897
+ "node": ">= 0.8"
1898
+ }
1899
+ },
1900
+ "node_modules/url-parse": {
1901
+ "version": "1.5.10",
1902
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
1903
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
1904
+ "license": "MIT",
1905
+ "dependencies": {
1906
+ "querystringify": "^2.1.1",
1907
+ "requires-port": "^1.0.0"
1908
+ }
1909
+ },
1910
+ "node_modules/utils-merge": {
1911
+ "version": "1.0.1",
1912
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1913
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1914
+ "license": "MIT",
1915
+ "engines": {
1916
+ "node": ">= 0.4.0"
1917
+ }
1918
+ },
1919
+ "node_modules/vary": {
1920
+ "version": "1.1.2",
1921
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1922
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1923
+ "license": "MIT",
1924
+ "engines": {
1925
+ "node": ">= 0.8"
1926
+ }
1927
+ },
1928
+ "node_modules/w3c-xmlserializer": {
1929
+ "version": "4.0.0",
1930
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
1931
+ "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
1932
+ "license": "MIT",
1933
+ "dependencies": {
1934
+ "xml-name-validator": "^4.0.0"
1935
+ },
1936
+ "engines": {
1937
+ "node": ">=14"
1938
+ }
1939
+ },
1940
+ "node_modules/web-streams-polyfill": {
1941
+ "version": "3.3.3",
1942
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
1943
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
1944
+ "license": "MIT",
1945
+ "engines": {
1946
+ "node": ">= 8"
1947
+ }
1948
+ },
1949
+ "node_modules/webidl-conversions": {
1950
+ "version": "7.0.0",
1951
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
1952
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
1953
+ "license": "BSD-2-Clause",
1954
+ "engines": {
1955
+ "node": ">=12"
1956
+ }
1957
+ },
1958
+ "node_modules/whatwg-encoding": {
1959
+ "version": "2.0.0",
1960
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
1961
+ "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
1962
+ "license": "MIT",
1963
+ "dependencies": {
1964
+ "iconv-lite": "0.6.3"
1965
+ },
1966
+ "engines": {
1967
+ "node": ">=12"
1968
+ }
1969
+ },
1970
+ "node_modules/whatwg-encoding/node_modules/iconv-lite": {
1971
+ "version": "0.6.3",
1972
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
1973
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
1974
+ "license": "MIT",
1975
+ "dependencies": {
1976
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
1977
+ },
1978
+ "engines": {
1979
+ "node": ">=0.10.0"
1980
+ }
1981
+ },
1982
+ "node_modules/whatwg-mimetype": {
1983
+ "version": "3.0.0",
1984
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
1985
+ "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
1986
+ "license": "MIT",
1987
+ "engines": {
1988
+ "node": ">=12"
1989
+ }
1990
+ },
1991
+ "node_modules/whatwg-url": {
1992
+ "version": "12.0.1",
1993
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
1994
+ "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
1995
+ "license": "MIT",
1996
+ "dependencies": {
1997
+ "tr46": "^4.1.1",
1998
+ "webidl-conversions": "^7.0.0"
1999
+ },
2000
+ "engines": {
2001
+ "node": ">=14"
2002
+ }
2003
+ },
2004
+ "node_modules/ws": {
2005
+ "version": "8.18.2",
2006
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz",
2007
+ "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==",
2008
+ "license": "MIT",
2009
+ "engines": {
2010
+ "node": ">=10.0.0"
2011
+ },
2012
+ "peerDependencies": {
2013
+ "bufferutil": "^4.0.1",
2014
+ "utf-8-validate": ">=5.0.2"
2015
+ },
2016
+ "peerDependenciesMeta": {
2017
+ "bufferutil": {
2018
+ "optional": true
2019
+ },
2020
+ "utf-8-validate": {
2021
+ "optional": true
2022
+ }
2023
+ }
2024
+ },
2025
+ "node_modules/xml-name-validator": {
2026
+ "version": "4.0.0",
2027
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
2028
+ "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
2029
+ "license": "Apache-2.0",
2030
+ "engines": {
2031
+ "node": ">=12"
2032
+ }
2033
+ },
2034
+ "node_modules/xmlchars": {
2035
+ "version": "2.2.0",
2036
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
2037
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
2038
+ "license": "MIT"
2039
+ }
2040
+ }
2041
+ }
package.json ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "notion2api-nodejs",
3
+ "version": "1.0.0",
4
+ "description": "Notion API client with lightweight browser-free option",
5
+ "main": "src/lightweight-client-express.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "node src/lightweight-client-express.js",
9
+ "dev": "nodemon src/lightweight-client-express.js",
10
+ "original": "node src/index.js"
11
+ },
12
+ "keywords": [
13
+ "notion",
14
+ "openai",
15
+ "api",
16
+ "bridge"
17
+ ],
18
+ "author": "",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "axios": "^1.9.0",
22
+ "chalk": "^4.1.2",
23
+ "dotenv": "^16.3.1",
24
+ "express": "^4.18.2",
25
+ "https-proxy-agent": "^7.0.2",
26
+ "jsdom": "^22.1.0",
27
+ "node-fetch": "^3.3.2",
28
+ "playwright": "^1.40.1"
29
+ },
30
+ "devDependencies": {
31
+ "nodemon": "^3.0.2"
32
+ }
33
+ }
proxy_server.log ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 2025/06/07 22:39:46.565674 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
2
+ 2025/06/07 22:39:46.581064 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
3
+ 2025/06/07 22:39:46.581064 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
4
+ 2025/06/07 22:39:46.581064 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
5
+ 2025/06/07 22:39:46.581064 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
6
+ 2025/06/07 22:39:46.581064 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
7
+ 2025/06/07 22:39:46.581064 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
8
+ 2025/06/07 22:39:46.585274 chrome_tls_proxy.go:716: 服务器启动失败: listen tcp :10655: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
9
+ 2025/06/07 22:40:34.956835 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
10
+ 2025/06/07 22:40:34.982142 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
11
+ 2025/06/07 22:40:34.982142 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
12
+ 2025/06/07 22:40:34.982142 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
13
+ 2025/06/07 22:40:34.982142 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
14
+ 2025/06/07 22:40:34.982142 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
15
+ 2025/06/07 22:40:34.982142 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
16
+ 2025/06/07 22:40:42.541508 chrome_tls_proxy.go:537: 处理流式请求: POST https://www.notion.so/api/v3/runInferenceTranscript
17
+ 2025/06/07 22:40:42.594747 chrome_tls_proxy.go:153: 成功解析域名: www.notion.so -> 198.18.0.48
18
+ 2025/06/07 22:40:42.594747 chrome_tls_proxy.go:187: 建立TCP连接到: www.notion.so:443 (IP: 198.18.0.48)
19
+ 2025/06/07 22:40:42.595977 chrome_tls_proxy.go:196: TCP连接成功建立到 198.18.0.48:443
20
+ 2025/06/07 22:40:43.212470 chrome_tls_proxy.go:214: TLS握手成功,使用Chrome指纹
21
+ 2025/06/07 22:40:43.212470 chrome_tls_proxy.go:218: 协商的协议: h2
22
+ 2025/06/07 22:40:43.213065 chrome_tls_proxy.go:222: 使用Chrome TLS指纹处理HTTP/2请求
23
+ 2025/06/07 22:40:50.606946 chrome_tls_proxy.go:606: 流式传输完成: POST https://www.notion.so/api/v3/runInferenceTranscript, 总计 5491 字节
24
+ 2025/06/07 22:41:28.720532 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
25
+ 2025/06/07 22:41:28.733708 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
26
+ 2025/06/07 22:41:28.733708 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
27
+ 2025/06/07 22:41:28.733708 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
28
+ 2025/06/07 22:41:28.733708 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
29
+ 2025/06/07 22:41:28.733708 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
30
+ 2025/06/07 22:41:28.733708 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
31
+ 2025/06/07 22:42:27.903516 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
32
+ 2025/06/07 22:42:27.916172 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
33
+ 2025/06/07 22:42:27.916172 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
34
+ 2025/06/07 22:42:27.916172 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
35
+ 2025/06/07 22:42:27.916172 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
36
+ 2025/06/07 22:42:27.916172 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
37
+ 2025/06/07 22:42:27.916172 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
38
+ 2025/06/07 22:43:04.738913 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
39
+ 2025/06/07 22:43:04.755351 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
40
+ 2025/06/07 22:43:04.755866 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
41
+ 2025/06/07 22:43:04.755866 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
42
+ 2025/06/07 22:43:04.755866 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
43
+ 2025/06/07 22:43:04.755866 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
44
+ 2025/06/07 22:43:04.755866 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
45
+ 2025/06/07 22:47:31.473790 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
46
+ 2025/06/07 22:47:31.506810 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
47
+ 2025/06/07 22:47:31.506810 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
48
+ 2025/06/07 22:47:31.506810 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
49
+ 2025/06/07 22:47:31.506810 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
50
+ 2025/06/07 22:47:31.506810 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
51
+ 2025/06/07 22:47:31.506810 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
52
+ 2025/06/07 22:47:34.873521 chrome_tls_proxy.go:537: 处理流式请求: POST https://www.notion.so/api/v3/runInferenceTranscript
53
+ 2025/06/07 22:47:34.893684 chrome_tls_proxy.go:153: 成功解析域名: www.notion.so -> 198.18.0.48
54
+ 2025/06/07 22:47:34.893684 chrome_tls_proxy.go:187: 建立TCP连接到: www.notion.so:443 (IP: 198.18.0.48)
55
+ 2025/06/07 22:47:34.894769 chrome_tls_proxy.go:196: TCP连接成功建立到 198.18.0.48:443
56
+ 2025/06/07 22:47:35.209971 chrome_tls_proxy.go:214: TLS握手成功,使用Chrome指纹
57
+ 2025/06/07 22:47:35.209971 chrome_tls_proxy.go:218: 协商的协议: h2
58
+ 2025/06/07 22:47:35.209971 chrome_tls_proxy.go:222: 使用Chrome TLS指纹处理HTTP/2请求
59
+ 2025/06/07 22:47:41.994124 chrome_tls_proxy.go:606: 流式传输完成: POST https://www.notion.so/api/v3/runInferenceTranscript, 总计 4791 字节
60
+ 2025/06/07 23:04:02.434223 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
61
+ 2025/06/07 23:04:02.441993 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
62
+ 2025/06/07 23:04:02.441993 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
63
+ 2025/06/07 23:04:02.441993 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
64
+ 2025/06/07 23:04:02.441993 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
65
+ 2025/06/07 23:04:02.441993 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
66
+ 2025/06/07 23:04:02.441993 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
67
+ 2025/06/07 23:08:08.021527 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
68
+ 2025/06/07 23:08:08.026578 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
69
+ 2025/06/07 23:08:08.026578 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
70
+ 2025/06/07 23:08:08.026578 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
71
+ 2025/06/07 23:08:08.026578 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
72
+ 2025/06/07 23:08:08.026578 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
73
+ 2025/06/07 23:08:08.026578 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
74
+ 2025/06/07 23:08:08.031126 chrome_tls_proxy.go:716: 服务器启动失败: listen tcp :10655: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
75
+ 2025/06/07 23:08:33.883353 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
76
+ 2025/06/07 23:08:33.887267 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
77
+ 2025/06/07 23:08:33.887267 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
78
+ 2025/06/07 23:08:33.887267 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
79
+ 2025/06/07 23:08:33.887267 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
80
+ 2025/06/07 23:08:33.887267 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
81
+ 2025/06/07 23:08:33.887267 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
82
+ 2025/06/07 23:08:33.887941 chrome_tls_proxy.go:716: 服务器启动失败: listen tcp :10655: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
83
+ 2025/06/07 23:09:49.976166 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
84
+ 2025/06/07 23:09:49.981223 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
85
+ 2025/06/07 23:09:49.981223 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
86
+ 2025/06/07 23:09:49.981223 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
87
+ 2025/06/07 23:09:49.981223 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
88
+ 2025/06/07 23:09:49.981223 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
89
+ 2025/06/07 23:09:49.981223 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
90
+ 2025/06/07 23:49:46.540100 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
91
+ 2025/06/07 23:49:46.545351 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
92
+ 2025/06/07 23:49:46.545351 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
93
+ 2025/06/07 23:49:46.545351 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
94
+ 2025/06/07 23:49:46.545351 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
95
+ 2025/06/07 23:49:46.545351 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
96
+ 2025/06/07 23:49:46.545351 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
97
+ 2025/06/07 23:49:46.545880 chrome_tls_proxy.go:716: 服务器启动失败: listen tcp :10655: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted.
98
+ 2025/06/07 23:50:04.636504 chrome_tls_proxy.go:706: Chrome TLS指纹代理服务器运行在 http://localhost:10655/proxy
99
+ 2025/06/07 23:50:04.640668 chrome_tls_proxy.go:707: 使用方法:向/proxy发送POST请求,请求体包含目标URL、方法、请求头和请求体
100
+ 2025/06/07 23:50:04.640668 chrome_tls_proxy.go:708: 支持流式响应,需要在请求体中添加 'stream': true
101
+ 2025/06/07 23:50:04.640668 chrome_tls_proxy.go:709: 已禁用速率限制:允许无限制请求
102
+ 2025/06/07 23:50:04.640668 chrome_tls_proxy.go:710: 已禁用并发限制:允许同一IP发起多个并发请求
103
+ 2025/06/07 23:50:04.640668 chrome_tls_proxy.go:711: 已强制使用IPv4连接,并使用8.8.8.8作为DNS服务器
104
+ 2025/06/07 23:50:04.640668 chrome_tls_proxy.go:712: 使用Chrome浏览器的TLS指纹进行连接
src/ProxyPool.js ADDED
@@ -0,0 +1,526 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios from 'axios';
2
+
3
+ /**
4
+ * 代理池类,用于管理和提供HTTP代理
5
+ */
6
+ class ProxyPool {
7
+ /**
8
+ * 创建代理池实例
9
+ * @param {Object} options - 配置选项
10
+ * @param {number} options.targetCount - 目标代理数量,默认20
11
+ * @param {number} options.batchSize - 每次获取的代理数量,默认20
12
+ * @param {number} options.testTimeout - 测试代理超时时间(毫秒),默认5000
13
+ * @param {number} options.requestTimeout - 请求目标网站超时时间(毫秒),默认10000
14
+ * @param {string} options.targetUrl - 目标网站URL,默认'https://www.notion.so'
15
+ * @param {number} options.concurrentRequests - 并发请求数量,默认10
16
+ * @param {number} options.minThreshold - 可用代理数量低于此阈值时自动补充,默认5
17
+ * @param {number} options.checkInterval - 检查代理池状态的时间间隔(毫秒),默认30000
18
+ * @param {string} options.proxyProtocol - 代理协议,默认'http'
19
+ * @param {number} options.maxRefillAttempts - 最大补充尝试次数,默认20
20
+ * @param {number} options.retryDelay - 重试延迟(毫秒),默认1000
21
+ * @param {boolean} options.useCache - 是否使用缓存,默认true
22
+ * @param {number} options.cacheExpiry - 缓存过期时间(毫秒),默认3600000 (1小时)
23
+ */
24
+ constructor(options = {}) {
25
+ // 配置参数
26
+ this.targetCount = options.targetCount || 20;
27
+ this.batchSize = options.batchSize || 20;
28
+ this.testTimeout = options.testTimeout || 5000;
29
+ this.requestTimeout = options.requestTimeout || 10000;
30
+ this.targetUrl = options.targetUrl || 'https://www.notion.so';
31
+ this.concurrentRequests = options.concurrentRequests || 10;
32
+ this.minThreshold = options.minThreshold || 5;
33
+ this.checkInterval = options.checkInterval || 30000; // 默认30秒检查一次
34
+ this.proxyProtocol = options.proxyProtocol || 'http';
35
+ this.maxRefillAttempts = options.maxRefillAttempts || 20; // 减少最大尝试次数
36
+ this.retryDelay = options.retryDelay || 1000; // 减少重试延迟
37
+ this.useCache = options.useCache !== undefined ? options.useCache : true;
38
+ this.cacheExpiry = options.cacheExpiry || 3600000; // 默认1小时
39
+
40
+ // 内部状态
41
+ this.availableProxies = [];
42
+ this.currentIndex = 0;
43
+ this.isInitialized = false;
44
+ this.isRefilling = false;
45
+ this.checkTimer = null;
46
+ this.proxyCache = new Map(); // 缓存验证过的代理
47
+
48
+ // 绑定方法
49
+ this.getProxy = this.getProxy.bind(this);
50
+ this.removeProxy = this.removeProxy.bind(this);
51
+ this.checkAndRefill = this.checkAndRefill.bind(this);
52
+ }
53
+
54
+ /**
55
+ * 初始化代理池
56
+ * @returns {Promise<void>}
57
+ */
58
+ async initialize() {
59
+ if (this.isInitialized) return;
60
+
61
+ console.log(`初始化代理池,目标数量: ${this.targetCount}`);
62
+ await this.refillProxies();
63
+
64
+ // 设置定时检查
65
+ this.checkTimer = setInterval(this.checkAndRefill, this.checkInterval);
66
+
67
+ this.isInitialized = true;
68
+ console.log(`代理池初始化完成,当前可用代理数量: ${this.availableProxies.length}`);
69
+ }
70
+
71
+ /**
72
+ * 停止代理池服务
73
+ */
74
+ stop() {
75
+ if (this.checkTimer) {
76
+ clearInterval(this.checkTimer);
77
+ this.checkTimer = null;
78
+ }
79
+ console.log('代理池服务已停止');
80
+ }
81
+
82
+ /**
83
+ * 检查并补充代理
84
+ */
85
+ async checkAndRefill() {
86
+ if (this.availableProxies.length <= this.minThreshold && !this.isRefilling) {
87
+ console.log(`可用代理数量(${this.availableProxies.length})低于阈值(${this.minThreshold}),开始补充代理`);
88
+ await this.refillProxies();
89
+ }
90
+ }
91
+
92
+ /**
93
+ * 补充代理到目标数量
94
+ * @returns {Promise<void>}
95
+ */
96
+ async refillProxies() {
97
+ if (this.isRefilling) return;
98
+
99
+ this.isRefilling = true;
100
+ console.log(`开始补充代理,当前数量: ${this.availableProxies.length},目标数量: ${this.targetCount}`);
101
+
102
+ let attempts = 0;
103
+
104
+ try {
105
+ // 计算需要补充的代理数量
106
+ const neededProxies = this.targetCount - this.availableProxies.length;
107
+
108
+ // 优先检查缓存中的代理
109
+ if (this.useCache && this.proxyCache.size > 0) {
110
+ await this.tryUsingCachedProxies(neededProxies);
111
+ }
112
+
113
+ // 如果缓存中的代理不足,继续获取新代理
114
+ while (this.availableProxies.length < this.targetCount && attempts < this.maxRefillAttempts) {
115
+ attempts++;
116
+
117
+ console.log(`补充尝试 #${attempts},当前可用代理: ${this.availableProxies.length}/${this.targetCount}`);
118
+
119
+ // 计算本次需要获取的批次大小
120
+ const remainingNeeded = this.targetCount - this.availableProxies.length;
121
+ const batchSizeNeeded = Math.max(this.batchSize, remainingNeeded * 2); // 获取更多代理以提高成功率
122
+
123
+ // 获取代理
124
+ const proxies = await this.getProxiesFromProvider(batchSizeNeeded);
125
+
126
+ if (proxies.length === 0) {
127
+ console.log(`没有获取到代理,等待${this.retryDelay/1000}秒后重试...`);
128
+ await new Promise(resolve => setTimeout(resolve, this.retryDelay));
129
+ continue;
130
+ }
131
+
132
+ // 过滤掉已有的代理
133
+ const newProxies = this.filterExistingProxies(proxies);
134
+
135
+ if (newProxies.length === 0) {
136
+ console.log('所有获取的代理都已存在,继续获取新代理...');
137
+ continue;
138
+ }
139
+
140
+ // 测试代理
141
+ const results = await this.testProxiesConcurrently(newProxies);
142
+
143
+ // 添加可用代理
144
+ this.addValidProxies(results);
145
+
146
+ // 如果已经获取到足够的代理,提前结束
147
+ if (this.availableProxies.length >= this.targetCount) {
148
+ break;
149
+ }
150
+
151
+ // 如果还没补充到足够的代理,等待一段时间再继续
152
+ if (this.availableProxies.length < this.targetCount) {
153
+ await new Promise(resolve => setTimeout(resolve, this.retryDelay));
154
+ }
155
+ }
156
+ } catch (error) {
157
+ console.error('补充代理过程中出错:', error);
158
+ } finally {
159
+ this.isRefilling = false;
160
+
161
+ if (this.availableProxies.length >= this.targetCount) {
162
+ console.log(`代理补充完成,当前可用代理: ${this.availableProxies.length}/${this.targetCount}`);
163
+ } else {
164
+ console.log(`已达到最大尝试次数 ${this.maxRefillAttempts},当前可用代理: ${this.availableProxies.length}/${this.targetCount}`);
165
+ }
166
+ }
167
+ }
168
+
169
+ /**
170
+ * 尝试使用缓存中的代理
171
+ * @param {number} neededProxies - 需要的代理数量
172
+ */
173
+ async tryUsingCachedProxies(neededProxies) {
174
+ const now = Date.now();
175
+ const cachedProxies = [];
176
+
177
+ // 筛选未过期的缓存代理
178
+ for (const [proxyKey, data] of this.proxyCache.entries()) {
179
+ if (now - data.timestamp < this.cacheExpiry && data.valid) {
180
+ cachedProxies.push(proxyKey);
181
+
182
+ if (cachedProxies.length >= neededProxies) {
183
+ break;
184
+ }
185
+ }
186
+ }
187
+
188
+ if (cachedProxies.length > 0) {
189
+ console.log(`从缓存中找到 ${cachedProxies.length} 个可能可用的代理`);
190
+
191
+ // 验证缓存的代理是否仍然可用
192
+ const results = await this.testProxiesConcurrently(cachedProxies);
193
+ this.addValidProxies(results);
194
+ }
195
+ }
196
+
197
+ /**
198
+ * 过滤掉已存在的代理
199
+ * @param {Array<string>} proxies - 代理列表
200
+ * @returns {Array<string>} - 新代理列表
201
+ */
202
+ filterExistingProxies(proxies) {
203
+ return proxies.filter(proxy => {
204
+ const [ip, port] = proxy.split(':');
205
+ return !this.availableProxies.some(p => p.ip === ip && p.port === port);
206
+ });
207
+ }
208
+
209
+ /**
210
+ * 添加有效的代理到代理池
211
+ * @param {Array<{proxy: string, result: boolean}>} results - 测试结果
212
+ */
213
+ addValidProxies(results) {
214
+ for (const { proxy, result } of results) {
215
+ if (result) {
216
+ const [ip, port] = proxy.split(':');
217
+
218
+ // 检查是否已存在
219
+ if (!this.availableProxies.some(p => p.ip === ip && p.port === port)) {
220
+ const proxyObj = {
221
+ ip,
222
+ port,
223
+ protocol: this.proxyProtocol,
224
+ full: `${this.proxyProtocol}://${proxy}`,
225
+ addedAt: new Date().toISOString()
226
+ };
227
+
228
+ this.availableProxies.push(proxyObj);
229
+
230
+ // 添加到缓存
231
+ if (this.useCache) {
232
+ this.proxyCache.set(proxy, {
233
+ valid: true,
234
+ timestamp: Date.now()
235
+ });
236
+ }
237
+
238
+ console.log(`成功添加代理: ${proxyObj.full},当前可用代理: ${this.availableProxies.length}/${this.targetCount}`);
239
+
240
+ if (this.availableProxies.length >= this.targetCount) {
241
+ break;
242
+ }
243
+ }
244
+ } else if (this.useCache) {
245
+ // 记录无效代理到缓存
246
+ this.proxyCache.set(proxy, {
247
+ valid: false,
248
+ timestamp: Date.now()
249
+ });
250
+ }
251
+ }
252
+ }
253
+
254
+ /**
255
+ * 从代理服务获取代理URL
256
+ * @param {number} count - 请求的代理数量
257
+ * @returns {Promise<Array<string>>} - 代理URL列表
258
+ */
259
+ async getProxiesFromProvider(count = null) {
260
+ try {
261
+ const requestCount = count || this.batchSize;
262
+ const url = `https://proxy.scdn.io/api/get_proxy.php?protocol=${this.proxyProtocol}&count=${requestCount}`;
263
+ console.log(`正在获取代理,URL: ${url}`);
264
+
265
+ const response = await axios.get(url, {
266
+ timeout: 10000,
267
+ validateStatus: status => true
268
+ });
269
+
270
+ if (response.data && response.data.code === 200) {
271
+ console.log(`成功获取 ${response.data.data.count} 个代理`);
272
+ return response.data.data.proxies;
273
+ } else {
274
+ console.error('获取代理失败:', response.data ? response.data.message : '未知错误');
275
+ return [];
276
+ }
277
+ } catch (error) {
278
+ console.error('获取代理出错:', error.message);
279
+ return [];
280
+ }
281
+ }
282
+
283
+ /**
284
+ * 并发测试多个代理
285
+ * @param {Array<string>} proxies - 代理列表
286
+ * @returns {Promise<Array<{proxy: string, result: boolean}>>} - 测试结果
287
+ */
288
+ async testProxiesConcurrently(proxies) {
289
+ const results = [];
290
+ const remainingNeeded = this.targetCount - this.availableProxies.length;
291
+
292
+ // 增加并发数以加快处理速度
293
+ const concurrentRequests = Math.min(this.concurrentRequests * 2, 20);
294
+
295
+ // 分批处理代理
296
+ for (let i = 0; i < proxies.length; i += concurrentRequests) {
297
+ const batch = proxies.slice(i, i + concurrentRequests);
298
+ const promises = batch.map(proxy => {
299
+ // 检查缓存中是否有近期验证过的结果
300
+ if (this.useCache && this.proxyCache.has(proxy)) {
301
+ const cachedResult = this.proxyCache.get(proxy);
302
+ const isFresh = (Date.now() - cachedResult.timestamp) < this.cacheExpiry;
303
+
304
+ if (isFresh) {
305
+ // 使用缓存结果,避免重复测试
306
+ return Promise.resolve({ proxy, result: cachedResult.valid });
307
+ }
308
+ }
309
+
310
+ return this.testProxy(proxy)
311
+ .then(result => ({ proxy, result }))
312
+ .catch(() => ({ proxy, result: false }));
313
+ });
314
+
315
+ const batchResults = await Promise.all(promises);
316
+ results.push(...batchResults);
317
+
318
+ // 如果已经找到足够的代理,提前结束测试
319
+ const successCount = results.filter(item => item.result).length;
320
+ if (successCount >= remainingNeeded) {
321
+ break;
322
+ }
323
+ }
324
+
325
+ return results;
326
+ }
327
+
328
+ /**
329
+ * 测试代理是否可用
330
+ * @param {string} proxyUrl - 代理URL
331
+ * @returns {Promise<boolean>} - 代理是否可用
332
+ */
333
+ async testProxy(proxyUrl) {
334
+ try {
335
+ // 创建代理配置
336
+ const proxyConfig = {
337
+ host: proxyUrl.split(':')[0],
338
+ port: parseInt(proxyUrl.split(':')[1]),
339
+ protocol: this.proxyProtocol
340
+ };
341
+
342
+ // 发送请求到目标网站
343
+ const response = await axios.get(this.targetUrl, {
344
+ proxy: proxyConfig,
345
+ headers: {
346
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
347
+ 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
348
+ 'Accept-Language': 'en-US,en;q=0.5',
349
+ 'Connection': 'keep-alive',
350
+ 'Upgrade-Insecure-Requests': '1'
351
+ },
352
+ timeout: this.requestTimeout,
353
+ validateStatus: status => true,
354
+ maxRedirects: 10,
355
+ followRedirect: true
356
+ });
357
+
358
+ // 检查响应是否包含目标网站特有的内容
359
+ const isTargetContent = response.data &&
360
+ (typeof response.data === 'string') &&
361
+ (response.data.includes('notion') ||
362
+ response.data.includes('Notion'));
363
+
364
+ const isValid = response.status === 200 && isTargetContent;
365
+
366
+ if (isValid) {
367
+ console.log(`代理 ${proxyUrl} 请求目标网站成功,状态码: ${response.status}`);
368
+ } else {
369
+ console.log(`代理 ${proxyUrl} 请求目标网站失败,状态码: ${response.status}`);
370
+ }
371
+
372
+ return isValid;
373
+ } catch (error) {
374
+ console.log(`代理 ${proxyUrl} 请求出错: ${error.message}`);
375
+ return false;
376
+ }
377
+ }
378
+
379
+ /**
380
+ * 获取一个可用代理
381
+ * @returns {Object|null} - 代理对象,如果没有可用代理则返回null
382
+ */
383
+ getProxy() {
384
+ if (this.availableProxies.length === 0) {
385
+ console.log('没有可用代理');
386
+ return null;
387
+ }
388
+
389
+ // 轮询方式获取代理
390
+ const proxy = this.availableProxies[this.currentIndex];
391
+ this.currentIndex = (this.currentIndex + 1) % this.availableProxies.length;
392
+
393
+ return proxy;
394
+ }
395
+
396
+ /**
397
+ * 移除指定代理
398
+ * @param {string} ip - 代理IP
399
+ * @param {string|number} port - 代理端口
400
+ * @returns {boolean} - 是否成功移除
401
+ */
402
+ removeProxy(ip, port) {
403
+ const portStr = port.toString();
404
+ const initialLength = this.availableProxies.length;
405
+
406
+ // 找到要移除的代理
407
+ const proxyToRemove = this.availableProxies.find(
408
+ proxy => proxy.ip === ip && proxy.port === portStr
409
+ );
410
+
411
+ if (proxyToRemove) {
412
+ // 更新缓存,标记为无效
413
+ if (this.useCache) {
414
+ const proxyKey = `${ip}:${portStr}`;
415
+ this.proxyCache.set(proxyKey, { valid: false, timestamp: Date.now() });
416
+ }
417
+ }
418
+
419
+ this.availableProxies = this.availableProxies.filter(
420
+ proxy => !(proxy.ip === ip && proxy.port === portStr)
421
+ );
422
+
423
+ // 重置当前索引,确保不会越界
424
+ if (this.currentIndex >= this.availableProxies.length && this.availableProxies.length > 0) {
425
+ this.currentIndex = 0;
426
+ }
427
+
428
+ const removed = initialLength > this.availableProxies.length;
429
+
430
+ if (removed) {
431
+ console.log(`已移除代理 ${ip}:${port},当前可用代理: ${this.availableProxies.length}`);
432
+ } else {
433
+ console.log(`未找到要移除的代理 ${ip}:${port}`);
434
+ }
435
+
436
+ // 如果移除后代理数量低于阈值,触发补充
437
+ this.checkAndRefill();
438
+
439
+ return removed;
440
+ }
441
+
442
+ /**
443
+ * 获取所有可用代理
444
+ * @returns {Array<Object>} - 代理对象数组
445
+ */
446
+ getAllProxies() {
447
+ return [...this.availableProxies];
448
+ }
449
+
450
+ /**
451
+ * 获取可用代理数量
452
+ * @returns {number} - 代理数量
453
+ */
454
+ getCount() {
455
+ return this.availableProxies.length;
456
+ }
457
+
458
+ /**
459
+ * 清理过期的缓存条目
460
+ */
461
+ cleanupCache() {
462
+ if (!this.useCache) return;
463
+
464
+ const now = Date.now();
465
+ let cleanupCount = 0;
466
+
467
+ for (const [key, data] of this.proxyCache.entries()) {
468
+ if (now - data.timestamp > this.cacheExpiry) {
469
+ this.proxyCache.delete(key);
470
+ cleanupCount++;
471
+ }
472
+ }
473
+
474
+ if (cleanupCount > 0) {
475
+ console.log(`清理了 ${cleanupCount} 个过期的缓存代理`);
476
+ }
477
+ }
478
+ }
479
+
480
+ // 使用示例
481
+ async function example() {
482
+ // 创建代理池实例
483
+ const proxyPool = new ProxyPool({
484
+ targetCount: 10, // 目标保持10个代理
485
+ minThreshold: 3, // 当可用代理少于3个时,自动补充
486
+ checkInterval: 60000, // 每60秒检查一次
487
+ targetUrl: 'https://www.notion.so',
488
+ concurrentRequests: 15, // 增加并发请求数
489
+ useCache: true, // 启用缓存
490
+ maxRefillAttempts: 15, // 减少最大尝试次数
491
+ retryDelay: 1000 // 减少重试延迟
492
+ });
493
+
494
+ // 初始化代理池
495
+ await proxyPool.initialize();
496
+
497
+ // 获取一个代理
498
+ const proxy = proxyPool.getProxy();
499
+ console.log('获取到代理:', proxy);
500
+
501
+ // 模拟使用一段时间后,移除一个代理
502
+ setTimeout(() => {
503
+ if (proxy) {
504
+ proxyPool.removeProxy(proxy.ip, proxy.port);
505
+ }
506
+
507
+ // 获取所有代理
508
+ const allProxies = proxyPool.getAllProxies();
509
+ console.log(`当前所有代理(${allProxies.length}):`, allProxies);
510
+
511
+ // 使用完毕后停止服务
512
+ setTimeout(() => {
513
+ proxyPool.stop();
514
+ console.log('代理池示例运行完毕');
515
+ }, 5000);
516
+ }, 5000);
517
+ }
518
+
519
+ // 如果直接运行此文件,则执行示例
520
+ if (typeof require !== 'undefined' && require.main === module) {
521
+ example().catch(err => console.error('示例运行出错:', err));
522
+ }
523
+
524
+ // 导出 ProxyPool 类和实例
525
+ export default ProxyPool;
526
+ export const proxyPool = new ProxyPool();
src/ProxyServer.js ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { spawn } from 'child_process';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import fs from 'fs';
5
+ import os from 'os';
6
+ import dotenv from 'dotenv';
7
+ import chalk from 'chalk';
8
+
9
+ // 获取当前文件的目录路径
10
+ const __filename = fileURLToPath(import.meta.url);
11
+ const __dirname = dirname(__filename);
12
+
13
+ // 加载环境变量
14
+ dotenv.config({ path: join(dirname(__dirname), '.env') });
15
+
16
+ // 日志配置
17
+ const logger = {
18
+ info: (message) => console.log(chalk.blue(`[ProxyServer] ${message}`)),
19
+ error: (message) => console.error(chalk.red(`[ProxyServer] ${message}`)),
20
+ warning: (message) => console.warn(chalk.yellow(`[ProxyServer] ${message}`)),
21
+ success: (message) => console.log(chalk.green(`[ProxyServer] ${message}`)),
22
+ };
23
+
24
+ class ProxyServer {
25
+ constructor() {
26
+ this.proxyProcess = null;
27
+ this.platform = process.env.PROXY_SERVER_PLATFORM || 'auto';
28
+ this.port = process.env.PROXY_SERVER_PORT || 10655;
29
+ this.logPath = process.env.PROXY_SERVER_LOG_PATH || './proxy_server.log';
30
+ this.enabled = process.env.ENABLE_PROXY_SERVER === 'true';
31
+ this.proxyAuthToken = process.env.PROXY_AUTH_TOKEN || 'default_token';
32
+ this.logStream = null;
33
+ }
34
+
35
+ // 获取当前系统平台
36
+ detectPlatform() {
37
+ if (this.platform !== 'auto') {
38
+ return this.platform;
39
+ }
40
+
41
+ const platform = os.platform();
42
+ const arch = os.arch();
43
+
44
+ if (platform === 'win32') {
45
+ return 'windows';
46
+ } else if (platform === 'linux') {
47
+ if (arch === 'arm64') {
48
+ return 'android';
49
+ } else {
50
+ return 'linux';
51
+ }
52
+ } else if (platform === 'android') {
53
+ return 'android';
54
+ } else {
55
+ logger.warning(`未知平台: ${platform}, ${arch}, 默认使用linux版本`);
56
+ return 'linux';
57
+ }
58
+ }
59
+
60
+ // 获取代理服务器可执行文件路径
61
+ getProxyServerPath() {
62
+ const platform = this.detectPlatform();
63
+ const proxyDir = join(__dirname, 'proxy');
64
+
65
+ switch (platform) {
66
+ case 'windows':
67
+ return join(proxyDir, 'chrome_proxy_server_windows_amd64.exe');
68
+ case 'linux':
69
+ return join(proxyDir, 'chrome_proxy_server_linux_amd64');
70
+ case 'android':
71
+ return join(proxyDir, 'chrome_proxy_server_android_arm64');
72
+ default:
73
+ logger.error(`不支持的平台: ${platform}`);
74
+ return null;
75
+ }
76
+ }
77
+
78
+ // 启动代理服务器
79
+ async start() {
80
+ if (!this.enabled) {
81
+ logger.info('代理服务器未启用,跳过启动');
82
+ return;
83
+ }
84
+
85
+ if (this.proxyProcess) {
86
+ logger.warning('代理服务器已经在运行中');
87
+ return;
88
+ }
89
+
90
+ const proxyServerPath = this.getProxyServerPath();
91
+ if (!proxyServerPath) {
92
+ logger.error('无法获取代理服务器路径');
93
+ return;
94
+ }
95
+
96
+ try {
97
+ // 确保可执行文件有执行权限(在Linux/Android上)
98
+ if (this.detectPlatform() !== 'windows') {
99
+ try {
100
+ fs.chmodSync(proxyServerPath, 0o755);
101
+ } catch (err) {
102
+ logger.warning(`无法设置执行权限: ${err.message}`);
103
+ }
104
+ }
105
+
106
+ // 创建日志文件
107
+ this.logStream = fs.createWriteStream(this.logPath, { flags: 'a' });
108
+
109
+ // 修复 stdio 参数问题
110
+ // 启动代理服务器进程
111
+ this.proxyProcess = spawn(proxyServerPath, [
112
+ '--port', this.port.toString(),
113
+ '--token', this.proxyAuthToken
114
+ ], {
115
+ stdio: ['ignore', 'pipe', 'pipe'], // 使用pipe而不是直接传递流
116
+ detached: false
117
+ });
118
+
119
+ // 将进程的输出重定向到日志文件
120
+ if (this.proxyProcess.stdout) {
121
+ this.proxyProcess.stdout.pipe(this.logStream);
122
+ }
123
+
124
+ if (this.proxyProcess.stderr) {
125
+ this.proxyProcess.stderr.pipe(this.logStream);
126
+ }
127
+
128
+ // 设置进程事件处理
129
+ this.proxyProcess.on('error', (err) => {
130
+ logger.error(`代理服务器启动失败: ${err.message}`);
131
+ this.proxyProcess = null;
132
+ if (this.logStream) {
133
+ this.logStream.end();
134
+ this.logStream = null;
135
+ }
136
+ });
137
+
138
+ this.proxyProcess.on('exit', (code, signal) => {
139
+ logger.info(`代理服务器已退出,退出码: ${code}, 信号: ${signal}`);
140
+ this.proxyProcess = null;
141
+ if (this.logStream) {
142
+ this.logStream.end();
143
+ this.logStream = null;
144
+ }
145
+ });
146
+
147
+ // 等待一段时间,确保服务器启动
148
+ await new Promise(resolve => setTimeout(resolve, 1000));
149
+
150
+ if (this.proxyProcess && this.proxyProcess.exitCode === null) {
151
+ logger.success(`代理服务器已启动,端口: ${this.port}, 日志文件: ${this.logPath}`);
152
+ return true;
153
+ } else {
154
+ logger.error('代理服务器启动失败');
155
+ if (this.logStream) {
156
+ this.logStream.end();
157
+ this.logStream = null;
158
+ }
159
+ return false;
160
+ }
161
+ } catch (error) {
162
+ logger.error(`启动代理服务器时出错: ${error.message}`);
163
+ if (this.logStream) {
164
+ this.logStream.end();
165
+ this.logStream = null;
166
+ }
167
+ return false;
168
+ }
169
+ }
170
+
171
+ // 停止代理服务器
172
+ stop() {
173
+ if (!this.proxyProcess) {
174
+ //logger.info('代理服务器已关闭');
175
+ return;
176
+ }
177
+
178
+ try {
179
+ // 在Windows上使用taskkill确保子进程也被终止
180
+ if (this.detectPlatform() === 'windows' && this.proxyProcess.pid) {
181
+ spawn('taskkill', ['/pid', this.proxyProcess.pid, '/f', '/t']);
182
+ } else {
183
+ // 在Linux/Android上使用kill信号
184
+ this.proxyProcess.kill('SIGTERM');
185
+ }
186
+
187
+ logger.success('代理服务器已停止');
188
+ } catch (error) {
189
+ logger.error(`停止代理服务器时出错: ${error.message}`);
190
+ } finally {
191
+ this.proxyProcess = null;
192
+ if (this.logStream) {
193
+ this.logStream.end();
194
+ this.logStream = null;
195
+ }
196
+ }
197
+ }
198
+ }
199
+
200
+ // 创建单例
201
+ const proxyServer = new ProxyServer();
202
+
203
+ // 导出
204
+ export { proxyServer };
src/lightweight-client-express.js ADDED
@@ -0,0 +1,248 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import express from 'express';
2
+ import dotenv from 'dotenv';
3
+ import { randomUUID } from 'crypto';
4
+ import { fileURLToPath } from 'url';
5
+ import { dirname, join } from 'path';
6
+ import chalk from 'chalk';
7
+ import {
8
+ ChatMessage, ChatCompletionRequest, Choice, ChoiceDelta, ChatCompletionChunk
9
+ } from './models.js';
10
+ import {
11
+ initialize,
12
+ streamNotionResponse,
13
+ buildNotionRequest,
14
+ FETCHED_IDS_SUCCESSFULLY
15
+ } from './lightweight-client.js';
16
+ import { proxyPool } from './ProxyPool.js';
17
+
18
+ // 获取当前文件的目录路径
19
+ const __filename = fileURLToPath(import.meta.url);
20
+ const __dirname = dirname(__filename);
21
+
22
+ // 加载环境变量
23
+ dotenv.config({ path: join(dirname(__dirname), '.env') });
24
+
25
+ // 日志配置
26
+ const logger = {
27
+ info: (message) => console.log(chalk.blue(`[info] ${message}`)),
28
+ error: (message) => console.error(chalk.red(`[error] ${message}`)),
29
+ warning: (message) => console.warn(chalk.yellow(`[warn] ${message}`)),
30
+ success: (message) => console.log(chalk.green(`[success] ${message}`)),
31
+ request: (method, path, status, time) => {
32
+ const statusColor = status >= 500 ? chalk.red :
33
+ status >= 400 ? chalk.yellow :
34
+ status >= 300 ? chalk.cyan :
35
+ status >= 200 ? chalk.green : chalk.white;
36
+ console.log(`${chalk.magenta(`[${method}]`)} - ${path} ${statusColor(status)} ${chalk.gray(`${time}ms`)}`);
37
+ }
38
+ };
39
+
40
+ // 认证配置
41
+ const EXPECTED_TOKEN = process.env.PROXY_AUTH_TOKEN || "default_token";
42
+
43
+ // 创建Express应用
44
+ const app = express();
45
+ app.use(express.json({ limit: '50mb' }));
46
+ app.use(express.urlencoded({ extended: true, limit: '50mb' }));
47
+
48
+ // 请求日志中间件
49
+ app.use((req, res, next) => {
50
+ const start = Date.now();
51
+
52
+ // 保存原始的 end 方法
53
+ const originalEnd = res.end;
54
+
55
+ // 重写 end 方法以记录请求完成时间
56
+ res.end = function(...args) {
57
+ const duration = Date.now() - start;
58
+ logger.request(req.method, req.path, res.statusCode, duration);
59
+ return originalEnd.apply(this, args);
60
+ };
61
+
62
+ next();
63
+ });
64
+
65
+ // 认证中间件
66
+ function authenticate(req, res, next) {
67
+ const authHeader = req.headers.authorization;
68
+
69
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
70
+ return res.status(401).json({
71
+ error: {
72
+ message: "Authentication required. Please provide a valid Bearer token.",
73
+ type: "authentication_error"
74
+ }
75
+ });
76
+ }
77
+
78
+ const token = authHeader.split(' ')[1];
79
+
80
+ if (token !== EXPECTED_TOKEN) {
81
+ return res.status(401).json({
82
+ error: {
83
+ message: "Invalid authentication credentials",
84
+ type: "authentication_error"
85
+ }
86
+ });
87
+ }
88
+
89
+ next();
90
+ }
91
+
92
+ // API路由
93
+
94
+ // 获取模型列表
95
+ app.get('/v1/models', authenticate, (req, res) => {
96
+ // 返回可用模型列表
97
+ const modelList = {
98
+ data: [
99
+ { id: "openai-gpt-4.1" },
100
+ { id: "anthropic-opus-4" },
101
+ { id: "anthropic-sonnet-4" },
102
+ { id: "anthropic-sonnet-3.x-stable" }
103
+ ]
104
+ };
105
+
106
+ res.json(modelList);
107
+ });
108
+
109
+ // 聊天完成端点
110
+ app.post('/v1/chat/completions', authenticate, async (req, res) => {
111
+ try {
112
+ // 检查是否成功获取Notion ID
113
+ if (!FETCHED_IDS_SUCCESSFULLY) {
114
+ return res.status(500).json({
115
+ error: {
116
+ message: "Notion ID获取失败。请检查您的NOTION_COOKIE是否有效,或者手动设置NOTION_SPACE_ID和NOTION_ACTIVE_USER_HEADER。",
117
+ type: "server_error"
118
+ }
119
+ });
120
+ }
121
+
122
+ // 验证请求数据
123
+ const requestData = req.body;
124
+
125
+ if (!requestData.messages || !Array.isArray(requestData.messages) || requestData.messages.length === 0) {
126
+ return res.status(400).json({
127
+ error: {
128
+ message: "Invalid request: 'messages' field must be a non-empty array.",
129
+ type: "invalid_request_error"
130
+ }
131
+ });
132
+ }
133
+
134
+ // 构建Notion请求
135
+ const notionRequestBody = buildNotionRequest(requestData);
136
+
137
+ // 处理流式响应
138
+ if (requestData.stream) {
139
+ res.setHeader('Content-Type', 'text/event-stream');
140
+ res.setHeader('Cache-Control', 'no-cache');
141
+ res.setHeader('Connection', 'keep-alive');
142
+
143
+ logger.info(`开始流式响应`);
144
+ const stream = await streamNotionResponse(notionRequestBody);
145
+ stream.pipe(res);
146
+
147
+ // 处理客户端断开连接
148
+ req.on('close', () => {
149
+ stream.end();
150
+ });
151
+ } else {
152
+ // 非流式响应
153
+ // 创建一个内部流来收集完整响应
154
+ logger.info(`开始非流式响应`);
155
+ const chunks = [];
156
+ const stream = await streamNotionResponse(notionRequestBody);
157
+
158
+ return new Promise((resolve, reject) => {
159
+ stream.on('data', (chunk) => {
160
+ const chunkStr = chunk.toString();
161
+ if (chunkStr.startsWith('data: ') && !chunkStr.includes('[DONE]')) {
162
+ try {
163
+ const dataJson = chunkStr.substring(6).trim();
164
+ if (dataJson) {
165
+ const chunkData = JSON.parse(dataJson);
166
+ if (chunkData.choices && chunkData.choices[0].delta && chunkData.choices[0].delta.content) {
167
+ chunks.push(chunkData.choices[0].delta.content);
168
+ }
169
+ }
170
+ } catch (error) {
171
+ logger.error(`解析非流式响应块时出错: ${error}`);
172
+ }
173
+ }
174
+ });
175
+
176
+ stream.on('end', () => {
177
+ const fullResponse = {
178
+ id: `chatcmpl-${randomUUID()}`,
179
+ object: "chat.completion",
180
+ created: Math.floor(Date.now() / 1000),
181
+ model: requestData.model,
182
+ choices: [
183
+ {
184
+ index: 0,
185
+ message: {
186
+ role: "assistant",
187
+ content: chunks.join('')
188
+ },
189
+ finish_reason: "stop"
190
+ }
191
+ ],
192
+ usage: {
193
+ prompt_tokens: null,
194
+ completion_tokens: null,
195
+ total_tokens: null
196
+ }
197
+ };
198
+
199
+ res.json(fullResponse);
200
+ resolve();
201
+ });
202
+
203
+ stream.on('error', (error) => {
204
+ logger.error(`非流式响应出错: ${error}`);
205
+ reject(error);
206
+ });
207
+ });
208
+ }
209
+ } catch (error) {
210
+ logger.error(`聊天完成端点错误: ${error}`);
211
+ res.status(500).json({
212
+ error: {
213
+ message: `Internal server error: ${error.message}`,
214
+ type: "server_error"
215
+ }
216
+ });
217
+ }
218
+ });
219
+
220
+ // 健康检查端点
221
+ app.get('/health', (req, res) => {
222
+ res.json({
223
+ status: 'ok',
224
+ timestamp: new Date().toISOString(),
225
+ notion_ids_fetched: FETCHED_IDS_SUCCESSFULLY
226
+ });
227
+ });
228
+
229
+ // 启动服务器
230
+ const PORT = process.env.PORT || 7860;
231
+
232
+ // 初始化并启动服务器
233
+ initialize().then(() => {
234
+ app.listen(PORT, () => {
235
+ logger.info(`服务已启动 - 端口: ${PORT}`);
236
+ logger.info(`访问地址: http://localhost:${PORT}`);
237
+
238
+ if (FETCHED_IDS_SUCCESSFULLY) {
239
+ logger.success(`Notion可用状态: ✅`);
240
+ } else {
241
+ logger.warning(`Notion可用状态: ❌`);
242
+ logger.warning(`警告: Notion ID未成功获取,API调用将无法正常工作`);
243
+ logger.warning(`请检查NOTION_COOKIE配置或手动设置NOTION_SPACE_ID和NOTION_ACTIVE_USER_HEADER`);
244
+ }
245
+ });
246
+ }).catch((error) => {
247
+ logger.error(`初始化失败: ${error}`);
248
+ });
src/lightweight-client.js ADDED
@@ -0,0 +1,941 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fetch from 'node-fetch';
2
+ import { JSDOM } from 'jsdom';
3
+ import dotenv from 'dotenv';
4
+ import { randomUUID } from 'crypto';
5
+ import { fileURLToPath } from 'url';
6
+ import { dirname, join } from 'path';
7
+ import { PassThrough } from 'stream';
8
+ import chalk from 'chalk';
9
+ import {
10
+ ChatMessage, ChatCompletionRequest, NotionTranscriptConfigValue,
11
+ NotionTranscriptContextValue, NotionTranscriptItem, NotionDebugOverrides,
12
+ NotionRequestBody, ChoiceDelta, Choice, ChatCompletionChunk, NotionTranscriptItemByuser
13
+ } from './models.js';
14
+ import { proxyPool } from './ProxyPool.js';
15
+ import { proxyServer } from './ProxyServer.js';
16
+ // 获取当前文件的目录路径
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = dirname(__filename);
19
+
20
+ // 加载环境变量
21
+ dotenv.config({ path: join(dirname(__dirname), '.env') });
22
+
23
+ // 日志配置
24
+ const logger = {
25
+ info: (message) => console.log(chalk.blue(`[info] ${message}`)),
26
+ error: (message) => console.error(chalk.red(`[error] ${message}`)),
27
+ warning: (message) => console.warn(chalk.yellow(`[warn] ${message}`)),
28
+ success: (message) => console.log(chalk.green(`[success] ${message}`)),
29
+ };
30
+
31
+ // 配置
32
+ const NOTION_API_URL = "https://www.notion.so/api/v3/runInferenceTranscript";
33
+ let NOTION_COOKIE = '';
34
+ let NOTION_SPACE_ID = process.env.NOTION_SPACE_ID;
35
+ let FETCHED_NOTION_USER_ID = process.env.NOTION_ACTIVE_USER_HEADER;
36
+ const USE_NATIVE_PROXY_POOL = process.env.USE_NATIVE_PROXY_POOL === 'true';
37
+ const ENABLE_PROXY_SERVER = process.env.ENABLE_PROXY_SERVER === 'true';
38
+ let proxy = null;
39
+ let isMoreThanOne = false;
40
+ let index = 0;
41
+
42
+
43
+ // 代理配置
44
+ const PROXY_URL = process.env.PROXY_URL || "";
45
+
46
+ // 标记是否成功获取ID
47
+ let FETCHED_IDS_SUCCESSFULLY = false;
48
+
49
+ // 注册进程退出事件,确保代理服务器在程序退出时关闭
50
+ process.on('exit', () => {
51
+ try {
52
+ if (proxyServer) {
53
+ proxyServer.stop();
54
+ }
55
+ } catch (error) {
56
+ logger.error(`程序退出时关闭代理服务器出错: ${error.message}`);
57
+ }
58
+ });
59
+
60
+ // 捕获意外退出信号
61
+ ['SIGINT', 'SIGTERM', 'SIGQUIT'].forEach(signal => {
62
+ process.on(signal, () => {
63
+ logger.info(`收到${signal}信号,正在关闭代理服务器...`);
64
+ try {
65
+ if (proxyServer) {
66
+ proxyServer.stop();
67
+ }
68
+ } catch (error) {
69
+ logger.error(`关闭代理服务器出错: ${error.message}`);
70
+ }
71
+ process.exit(0);
72
+ });
73
+ });
74
+
75
+ function getCookie() {
76
+ const cookies = process.env.NOTION_COOKIE;
77
+ if (cookies) {
78
+ let cookiePairs = cookies.split('|');
79
+ if (cookiePairs.length > 1){
80
+ isMoreThanOne=true;
81
+ }
82
+ let currentIndex = index;
83
+ index++;
84
+ if (index >= cookiePairs.length) {
85
+ index = 0;
86
+ }
87
+ return cookiePairs[currentIndex];
88
+ }
89
+ return '';
90
+ }
91
+
92
+ // 添加一个通用的cookie解析函数
93
+ function parseCookiesFromString(cookieString, logPrefix = "") {
94
+ const cookiesToAdd = {};
95
+
96
+ if (!cookieString || typeof cookieString !== 'string') {
97
+ logger.error(`${logPrefix}Invalid cookie string provided`);
98
+ return cookiesToAdd;
99
+ }
100
+
101
+ // 按分号分割cookie字符串
102
+ const cookiePairs = cookieString.split(';');
103
+ logger.info(`${logPrefix}Parsing ${cookiePairs.length} cookie pairs`);
104
+
105
+ for (let i = 0; i < cookiePairs.length; i++) {
106
+ try {
107
+ const pair = cookiePairs[i].trim();
108
+ if (!pair) continue;
109
+
110
+ // 查找第一个等号的位置
111
+ const firstEqualPos = pair.indexOf('=');
112
+ if (firstEqualPos <= 0) {
113
+ logger.warning(`${logPrefix}Skipping invalid cookie pair: ${pair}`);
114
+ continue;
115
+ }
116
+
117
+ // 分割名称和值
118
+ const name = pair.substring(0, firstEqualPos).trim();
119
+ const value = pair.substring(firstEqualPos + 1).trim();
120
+
121
+ if (!name || !value) {
122
+ logger.warning(`${logPrefix}Skipping cookie with empty name or value: ${pair}`);
123
+ continue;
124
+ }
125
+
126
+ logger.info(`${logPrefix}Parsed cookie: ${name} (length: ${value.length})`);
127
+
128
+ cookiesToAdd[name] = value;
129
+ } catch (cookieError) {
130
+ logger.warning(`${logPrefix}Error parsing cookie pair at index ${i}: ${cookieError}`);
131
+ }
132
+ }
133
+
134
+ logger.info(`${logPrefix}Successfully parsed ${Object.keys(cookiesToAdd).length} cookies`);
135
+ return cookiesToAdd;
136
+ }
137
+
138
+ // 获取Notion空间ID和用户ID
139
+ async function fetchAndSetNotionIds() {
140
+ if (!NOTION_COOKIE) {
141
+ logger.error(`无法获取Notion ID: 未设置NOTION_COOKIE`);
142
+ FETCHED_IDS_SUCCESSFULLY = false;
143
+ return;
144
+ }
145
+
146
+ try {
147
+ logger.info(`正在设置JSDOM环境...`);
148
+
149
+ // 创建JSDOM实例模拟浏览器环境
150
+ const dom = new JSDOM("", {
151
+ url: "https://www.notion.so",
152
+ referrer: "https://www.notion.so/",
153
+ contentType: "text/html",
154
+ includeNodeLocations: true,
155
+ storageQuota: 10000000,
156
+ pretendToBeVisual: true,
157
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
158
+ });
159
+
160
+ // 设置全局对象
161
+ const { window } = dom;
162
+
163
+ // 使用更安全的方式设置全局对象
164
+ try {
165
+ if (!global.window) {
166
+ global.window = window;
167
+ }
168
+
169
+ if (!global.document) {
170
+ global.document = window.document;
171
+ }
172
+
173
+ // 安全地设置navigator
174
+ if (!global.navigator) {
175
+ try {
176
+ Object.defineProperty(global, 'navigator', {
177
+ value: window.navigator,
178
+ writable: true,
179
+ configurable: true
180
+ });
181
+ } catch (navError) {
182
+ logger.warning(`无法设置navigator: ${navError.message},继续执行`);
183
+ // 继续执行,不会中断流程
184
+ }
185
+ }
186
+ } catch (globalError) {
187
+ logger.warning(`设置全局对象时出错: ${globalError.message}`);
188
+ }
189
+
190
+ // 设置cookie
191
+ document.cookie = NOTION_COOKIE;
192
+
193
+ // 创建fetch选项
194
+ const fetchOptions = {
195
+ method: 'POST',
196
+ headers: {
197
+ 'Content-Type': 'application/json',
198
+ 'accept': '*/*',
199
+ 'accept-language': 'en-US,en;q=0.9',
200
+ 'notion-audit-log-platform': 'web',
201
+ 'notion-client-version': '23.13.0.3686',
202
+ 'origin': 'https://www.notion.so',
203
+ 'referer': 'https://www.notion.so/',
204
+ 'user-agent': window.navigator.userAgent,
205
+ 'Cookie': NOTION_COOKIE
206
+ },
207
+ body: JSON.stringify({}),
208
+ };
209
+
210
+ // 添加代理配置(如果有)
211
+ if (PROXY_URL) {
212
+ const { HttpsProxyAgent } = await import('https-proxy-agent');
213
+ fetchOptions.agent = new HttpsProxyAgent(PROXY_URL);
214
+ logger.info(`使用代理: ${PROXY_URL}`);
215
+ }
216
+ logger.info(`正在获取Notion用户/空间ID...`);
217
+
218
+ // 发送请求
219
+ const response = await fetch("https://www.notion.so/api/v3/getSpaces", fetchOptions);
220
+
221
+ if (!response.ok) {
222
+ throw new Error(`HTTP error! status: ${response.status}`);
223
+ }
224
+
225
+ const data = await response.json();
226
+
227
+ // 提取用户ID
228
+ const userIdKey = Object.keys(data)[0];
229
+ if (!userIdKey) {
230
+ logger.error(`无法从响应中提取用户ID`);
231
+ FETCHED_IDS_SUCCESSFULLY = false;
232
+ return;
233
+ }
234
+
235
+ FETCHED_NOTION_USER_ID = userIdKey;
236
+ logger.success(`已获取Notion用户ID`);
237
+
238
+ // 提取空间ID
239
+ const userRoot = data[userIdKey]?.user_root?.[userIdKey];
240
+ const spaceViewPointers = userRoot?.value?.value?.space_view_pointers;
241
+
242
+ if (spaceViewPointers && Array.isArray(spaceViewPointers) && spaceViewPointers.length > 0) {
243
+ NOTION_SPACE_ID = spaceViewPointers[0].spaceId;
244
+
245
+ if (NOTION_SPACE_ID) {
246
+ logger.success(`已获取Notion空间ID`);
247
+ FETCHED_IDS_SUCCESSFULLY = true;
248
+ } else {
249
+ logger.error(`无法从space_view_pointers中提取spaceId`);
250
+ FETCHED_IDS_SUCCESSFULLY = false;
251
+ }
252
+ } else {
253
+ logger.error(`在响应中找不到space_view_pointers或spaceId`);
254
+ FETCHED_IDS_SUCCESSFULLY = false;
255
+ }
256
+
257
+ // 清理全局对象
258
+ try {
259
+ if (global.window) delete global.window;
260
+ if (global.document) delete global.document;
261
+
262
+ // 安全地删除navigator
263
+ if (global.navigator) {
264
+ try {
265
+ delete global.navigator;
266
+ } catch (navError) {
267
+ // 如果无法删除,尝试将其设置为undefined
268
+ try {
269
+ Object.defineProperty(global, 'navigator', {
270
+ value: undefined,
271
+ writable: true,
272
+ configurable: true
273
+ });
274
+ } catch (defineError) {
275
+ logger.warning(`无法清理navigator: ${defineError.message}`);
276
+ }
277
+ }
278
+ }
279
+ } catch (cleanupError) {
280
+ logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
281
+ }
282
+
283
+ } catch (error) {
284
+ logger.error(`获取Notion ID时出错: ${error}`);
285
+ FETCHED_IDS_SUCCESSFULLY = false;
286
+
287
+ // 确保清理全局对象
288
+ try {
289
+ if (global.window) delete global.window;
290
+ if (global.document) delete global.document;
291
+
292
+ // 安全地删除navigator
293
+ if (global.navigator) {
294
+ try {
295
+ delete global.navigator;
296
+ } catch (navError) {
297
+ // 如果无法删除,尝试将其设置为undefined
298
+ try {
299
+ Object.defineProperty(global, 'navigator', {
300
+ value: undefined,
301
+ writable: true,
302
+ configurable: true
303
+ });
304
+ } catch (defineError) {
305
+ logger.warning(`无法清理navigator: ${defineError.message}`);
306
+ }
307
+ }
308
+ }
309
+ } catch (cleanupError) {
310
+ logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
311
+ }
312
+ }
313
+ }
314
+
315
+ // 构建Notion请求
316
+ function buildNotionRequest(requestData) {
317
+ // 当前时间
318
+ const now = new Date();
319
+ // 格式化为ISO字符串,确保包含毫秒和时区
320
+ const isoString = now.toISOString();
321
+
322
+ // 生成随机名称,类似于Python版本
323
+ const randomWords = ["Project", "Workspace", "Team", "Studio", "Lab", "Hub", "Zone", "Space"];
324
+ const userName = `User${Math.floor(Math.random() * 900) + 100}`; // 生成100-999之间的随机数
325
+ const spaceName = `${randomWords[Math.floor(Math.random() * randomWords.length)]} ${Math.floor(Math.random() * 99) + 1}`;
326
+
327
+ // 创建transcript数组
328
+ const transcript = [];
329
+
330
+ // 添加配置项
331
+ if(requestData.model === 'anthropic-sonnet-3.x-stable'){
332
+ transcript.push(new NotionTranscriptItem({
333
+ type: "config",
334
+ value: new NotionTranscriptConfigValue({
335
+ })
336
+ }));
337
+ }else{
338
+ transcript.push(new NotionTranscriptItem({
339
+ type: "config",
340
+ value: new NotionTranscriptConfigValue({
341
+ model: requestData.model
342
+ })
343
+ }));
344
+ }
345
+
346
+
347
+ // 添加上下文项
348
+ transcript.push(new NotionTranscriptItem({
349
+ type: "context",
350
+ value: new NotionTranscriptContextValue({
351
+ userId: FETCHED_NOTION_USER_ID,
352
+ spaceId: NOTION_SPACE_ID,
353
+ surface: "home_module",
354
+ timezone: "America/Los_Angeles",
355
+ userName: userName,
356
+ spaceName: spaceName,
357
+ spaceViewId: randomUUID(),
358
+ currentDatetime: isoString
359
+ })
360
+ }));
361
+
362
+ // 添加agent-integration项
363
+ transcript.push(new NotionTranscriptItem({
364
+ type: "agent-integration"
365
+ }));
366
+
367
+ // 添加消息
368
+ for (const message of requestData.messages) {
369
+ // 处理消息内容,确保格式一致
370
+ let content = message.content;
371
+
372
+ // 处理内容为数组的情况
373
+ if (Array.isArray(content)) {
374
+ let textContent = "";
375
+ for (const part of content) {
376
+ if (part && typeof part === 'object' && part.type === 'text') {
377
+ if (typeof part.text === 'string') {
378
+ textContent += part.text;
379
+ }
380
+ }
381
+ }
382
+ content = textContent || ""; // 使用提取的文本或空字符串
383
+ } else if (typeof content !== 'string') {
384
+ content = ""; // 如果不是字符串或数组,则默认为空字符串
385
+ }
386
+
387
+ if (message.role === "system") {
388
+ // 系统消息作为用户消息添加
389
+ transcript.push(new NotionTranscriptItemByuser({
390
+ type: "user",
391
+ value: [[content]],
392
+ userId: FETCHED_NOTION_USER_ID,
393
+ createdAt: message.createdAt || isoString
394
+ }));
395
+ } else if (message.role === "user") {
396
+ // 用户消息
397
+ transcript.push(new NotionTranscriptItemByuser({
398
+ type: "user",
399
+ value: [[content]],
400
+ userId: FETCHED_NOTION_USER_ID,
401
+ createdAt: message.createdAt || isoString
402
+ }));
403
+ } else if (message.role === "assistant") {
404
+ // 助手消息
405
+ transcript.push(new NotionTranscriptItem({
406
+ type: "markdown-chat",
407
+ value: content,
408
+ traceId: message.traceId || randomUUID(),
409
+ createdAt: message.createdAt || isoString
410
+ }));
411
+ }
412
+ }
413
+
414
+ // 创建请求体
415
+ return new NotionRequestBody({
416
+ spaceId: NOTION_SPACE_ID,
417
+ transcript: transcript,
418
+ createThread: true,
419
+ traceId: randomUUID(),
420
+ debugOverrides: new NotionDebugOverrides({
421
+ cachedInferences: {},
422
+ annotationInferences: {},
423
+ emitInferences: false
424
+ }),
425
+ generateTitle: false,
426
+ saveAllThreadOperations: false
427
+ });
428
+ }
429
+
430
+ // 流式处理Notion响应
431
+ async function streamNotionResponse(notionRequestBody) {
432
+ // 创建流
433
+ const stream = new PassThrough();
434
+
435
+ // 添加初始数据,确保连接建立
436
+ stream.write(':\n\n'); // 发送一个空注释行,保持连接活跃
437
+
438
+ // 设置HTTP头模板
439
+ const headers = {
440
+ 'Content-Type': 'application/json',
441
+ 'accept': 'application/x-ndjson',
442
+ 'accept-language': 'en-US,en;q=0.9',
443
+ 'notion-audit-log-platform': 'web',
444
+ 'notion-client-version': '23.13.0.3686',
445
+ 'origin': 'https://www.notion.so',
446
+ 'referer': 'https://www.notion.so/chat',
447
+ 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
448
+ 'x-notion-active-user-header': FETCHED_NOTION_USER_ID,
449
+ 'x-notion-space-id': NOTION_SPACE_ID
450
+ };
451
+
452
+ // 设置超时处理,确保流不会无限等待
453
+ const timeoutId = setTimeout(() => {
454
+ logger.warning(`请求超时,30秒内未收到响应`);
455
+ try {
456
+ // 发送结束消息
457
+ const endChunk = new ChatCompletionChunk({
458
+ choices: [
459
+ new Choice({
460
+ delta: new ChoiceDelta({ content: "请求超时,未收到Notion响应。" }),
461
+ finish_reason: "timeout"
462
+ })
463
+ ]
464
+ });
465
+ stream.write(`data: ${JSON.stringify(endChunk)}\n\n`);
466
+ stream.write('data: [DONE]\n\n');
467
+ stream.end();
468
+ } catch (error) {
469
+ logger.error(`发送超时消息时出错: ${error}`);
470
+ stream.end();
471
+ }
472
+ }, 30000); // 30秒超时
473
+
474
+ // 启动fetch处理
475
+ fetchNotionResponse(
476
+ stream,
477
+ notionRequestBody,
478
+ headers,
479
+ NOTION_API_URL,
480
+ NOTION_COOKIE,
481
+ timeoutId
482
+ ).catch((error) => {
483
+ logger.error(`流处理出错: ${error}`);
484
+ clearTimeout(timeoutId); // 清除超时计时器
485
+
486
+ try {
487
+ // 发送错误消息
488
+ const errorChunk = new ChatCompletionChunk({
489
+ choices: [
490
+ new Choice({
491
+ delta: new ChoiceDelta({ content: `处理请求时出错: ${error.message}` }),
492
+ finish_reason: "error"
493
+ })
494
+ ]
495
+ });
496
+ stream.write(`data: ${JSON.stringify(errorChunk)}\n\n`);
497
+ stream.write('data: [DONE]\n\n');
498
+ } catch (e) {
499
+ logger.error(`发送错误消息时出错: ${e}`);
500
+ } finally {
501
+ stream.end();
502
+ }
503
+ });
504
+
505
+ return stream;
506
+ }
507
+
508
+ // 使用fetch调用Notion API并处理流式响应
509
+ async function fetchNotionResponse(chunkQueue, notionRequestBody, headers, notionApiUrl, notionCookie, timeoutId) {
510
+ let responseReceived = false;
511
+ let dom = null;
512
+
513
+ try {
514
+ // 创建JSDOM实例模拟浏览器环境
515
+ dom = new JSDOM("", {
516
+ url: "https://www.notion.so",
517
+ referrer: "https://www.notion.so/chat",
518
+ contentType: "text/html",
519
+ includeNodeLocations: true,
520
+ storageQuota: 10000000,
521
+ pretendToBeVisual: true,
522
+ userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36"
523
+ });
524
+
525
+ // 设置全局对象
526
+ const { window } = dom;
527
+
528
+ // 使用更安全的方式设置全局对象
529
+ try {
530
+ if (!global.window) {
531
+ global.window = window;
532
+ }
533
+
534
+ if (!global.document) {
535
+ global.document = window.document;
536
+ }
537
+
538
+ // 安全地设置navigator
539
+ if (!global.navigator) {
540
+ try {
541
+ Object.defineProperty(global, 'navigator', {
542
+ value: window.navigator,
543
+ writable: true,
544
+ configurable: true
545
+ });
546
+ } catch (navError) {
547
+ logger.warning(`无法设置navigator: ${navError.message},继续执行`);
548
+ // 继续执行,不会中断流程
549
+ }
550
+ }
551
+ } catch (globalError) {
552
+ logger.warning(`设置全局对象时出错: ${globalError.message}`);
553
+ }
554
+
555
+ // 设置cookie
556
+ document.cookie = notionCookie;
557
+
558
+ //logger.info(`JSDOM环境已设置`);
559
+
560
+ // 创建fetch选项
561
+ const fetchOptions = {
562
+ method: 'POST',
563
+ headers: {
564
+ ...headers,
565
+ 'user-agent': window.navigator.userAgent,
566
+ 'Cookie': notionCookie
567
+ },
568
+ body: JSON.stringify(notionRequestBody),
569
+ };
570
+
571
+ // 添加代理配置(如果有)
572
+ if (USE_NATIVE_PROXY_POOL) {
573
+ proxy = proxyPool.getProxy();
574
+ if (proxy !== null)
575
+ {
576
+ const { HttpsProxyAgent } = await import('https-proxy-agent');
577
+ fetchOptions.agent = new HttpsProxyAgent(proxy.full);
578
+ logger.info(`使用代理: ${proxy.full}`);
579
+ }
580
+ else{
581
+ logger.warning(`没有可用代理`);
582
+ }
583
+ } else if(PROXY_URL) {
584
+ const { HttpsProxyAgent } = await import('https-proxy-agent');
585
+ fetchOptions.agent = new HttpsProxyAgent(PROXY_URL);
586
+ logger.info(`使用代理: ${PROXY_URL}`);
587
+ }
588
+ let response = null;
589
+ // 发送请求
590
+ //const response = await fetch(notionApiUrl, fetchOptions);
591
+ if (ENABLE_PROXY_SERVER){
592
+ response = await fetch('http://127.0.0.1:10655/proxy', {
593
+ method: 'POST',
594
+ body: JSON.stringify({
595
+ method: 'POST',
596
+ url: notionApiUrl,
597
+ headers: fetchOptions.headers,
598
+ body: fetchOptions.body,
599
+ stream:true
600
+ }),
601
+ });
602
+ }else{
603
+ response = await fetch(notionApiUrl, fetchOptions);
604
+ }
605
+
606
+
607
+ if (!response.ok) {
608
+ throw new Error(`HTTP error! status: ${response.status}`);
609
+ }
610
+
611
+ // 处理流式响应
612
+ if (!response.body) {
613
+ throw new Error("Response body is null");
614
+ }
615
+
616
+ // 创建流读取器
617
+ const reader = response.body;
618
+ let buffer = '';
619
+
620
+ // 处理数据块
621
+ reader.on('data', (chunk) => {
622
+ try {
623
+ // 标记已收到响应
624
+ if (!responseReceived) {
625
+ responseReceived = true;
626
+ logger.info(`已连接Notion API`);
627
+ clearTimeout(timeoutId); // 清除超时计时器
628
+ }
629
+
630
+ // 解码数据
631
+ const text = chunk.toString('utf8');
632
+ buffer += text;
633
+
634
+ // 按行分割并处理完整的JSON对象
635
+ const lines = buffer.split('\n');
636
+ buffer = lines.pop() || ''; // 保留最后一行(可能不完整)
637
+
638
+ for (const line of lines) {
639
+ if (!line.trim()) continue;
640
+
641
+ try {
642
+ const jsonData = JSON.parse(line);
643
+
644
+ // 提取内容
645
+ if (jsonData?.type === "markdown-chat" && typeof jsonData?.value === "string") {
646
+ const content = jsonData.value;
647
+ if (!content) continue;
648
+
649
+ // 创建OpenAI格式的块
650
+ const chunk = new ChatCompletionChunk({
651
+ choices: [
652
+ new Choice({
653
+ delta: new ChoiceDelta({ content }),
654
+ finish_reason: null
655
+ })
656
+ ]
657
+ });
658
+
659
+ // 添加到队列
660
+ const dataStr = `data: ${JSON.stringify(chunk)}\n\n`;
661
+ chunkQueue.write(dataStr);
662
+ } else if (jsonData?.recordMap) {
663
+ // 忽略recordMap响应
664
+ } else {
665
+ // 忽略其他类型响应
666
+ }
667
+ } catch (jsonError) {
668
+ logger.error(`解析JSON出错: ${jsonError}`);
669
+ }
670
+ }
671
+ } catch (error) {
672
+ logger.error(`处理数据块出错: ${error}`);
673
+ }
674
+ });
675
+
676
+ // 处理流结束
677
+ reader.on('end', () => {
678
+ try {
679
+ logger.info(`响应完成`);
680
+ if (isMoreThanOne){
681
+ NOTION_COOKIE = getCookie(); // 获取下一个cookie
682
+ logger.info('开始切换cookie和ID');
683
+ fetchAndSetNotionIds(); // 切换到下一个ID
684
+ }
685
+
686
+ // 如果没有收到任何响应,发送一个提示消息
687
+ if (!responseReceived) {
688
+ logger.warning(`未从Notion收到内容响应,请更换ip重试`);
689
+ if (USE_NATIVE_PROXY_POOL) {
690
+ proxyPool.removeProxy(proxy.ip, proxy.port);
691
+ }
692
+ const noContentChunk = new ChatCompletionChunk({
693
+ choices: [
694
+ new Choice({
695
+ delta: new ChoiceDelta({ content: "未从Notion收到内容响应,请更换ip重试。" }),
696
+ finish_reason: "no_content"
697
+ })
698
+ ]
699
+ });
700
+ chunkQueue.write(`data: ${JSON.stringify(noContentChunk)}\n\n`);
701
+ }
702
+
703
+ // 创建结束块
704
+ const endChunk = new ChatCompletionChunk({
705
+ choices: [
706
+ new Choice({
707
+ delta: new ChoiceDelta({ content: null }),
708
+ finish_reason: "stop"
709
+ })
710
+ ]
711
+ });
712
+
713
+ // 添加到队列
714
+ chunkQueue.write(`data: ${JSON.stringify(endChunk)}\n\n`);
715
+ chunkQueue.write('data: [DONE]\n\n');
716
+
717
+ // 清除超时计时器(如果尚未清除)
718
+ if (timeoutId) clearTimeout(timeoutId);
719
+
720
+ // 清理全局对象
721
+ try {
722
+ if (global.window) delete global.window;
723
+ if (global.document) delete global.document;
724
+
725
+ // 安全地删除navigator
726
+ if (global.navigator) {
727
+ try {
728
+ delete global.navigator;
729
+ } catch (navError) {
730
+ // 如果无法删除,尝试将其设置为undefined
731
+ try {
732
+ Object.defineProperty(global, 'navigator', {
733
+ value: undefined,
734
+ writable: true,
735
+ configurable: true
736
+ });
737
+ } catch (defineError) {
738
+ logger.warning(`无法清理navigator: ${defineError.message}`);
739
+ }
740
+ }
741
+ }
742
+ } catch (cleanupError) {
743
+ logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
744
+ }
745
+
746
+ // 结束流
747
+ chunkQueue.end();
748
+ } catch (error) {
749
+ logger.error(`Error in stream end handler: ${error}`);
750
+ if (timeoutId) clearTimeout(timeoutId);
751
+
752
+ // 清理全局对象
753
+ try {
754
+ if (global.window) delete global.window;
755
+ if (global.document) delete global.document;
756
+
757
+ // 安全地删除navigator
758
+ if (global.navigator) {
759
+ try {
760
+ delete global.navigator;
761
+ } catch (navError) {
762
+ // 如果无法删除,尝试将其设置为undefined
763
+ try {
764
+ Object.defineProperty(global, 'navigator', {
765
+ value: undefined,
766
+ writable: true,
767
+ configurable: true
768
+ });
769
+ } catch (defineError) {
770
+ logger.warning(`无法清理navigator: ${defineError.message}`);
771
+ }
772
+ }
773
+ }
774
+ } catch (cleanupError) {
775
+ logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
776
+ }
777
+
778
+ chunkQueue.end();
779
+ }
780
+ });
781
+
782
+ // 处理错误
783
+ reader.on('error', (error) => {
784
+ logger.error(`Stream error: ${error}`);
785
+ if (timeoutId) clearTimeout(timeoutId);
786
+
787
+ // 清理全局对象
788
+ try {
789
+ if (global.window) delete global.window;
790
+ if (global.document) delete global.document;
791
+
792
+ // 安全地删除navigator
793
+ if (global.navigator) {
794
+ try {
795
+ delete global.navigator;
796
+ } catch (navError) {
797
+ // 如果无法删除,尝试将其设置为undefined
798
+ try {
799
+ Object.defineProperty(global, 'navigator', {
800
+ value: undefined,
801
+ writable: true,
802
+ configurable: true
803
+ });
804
+ } catch (defineError) {
805
+ logger.warning(`无法清理navigator: ${defineError.message}`);
806
+ }
807
+ }
808
+ }
809
+ } catch (cleanupError) {
810
+ logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
811
+ }
812
+
813
+ try {
814
+ const errorChunk = new ChatCompletionChunk({
815
+ choices: [
816
+ new Choice({
817
+ delta: new ChoiceDelta({ content: `流读取错误: ${error.message}` }),
818
+ finish_reason: "error"
819
+ })
820
+ ]
821
+ });
822
+ chunkQueue.write(`data: ${JSON.stringify(errorChunk)}\n\n`);
823
+ chunkQueue.write('data: [DONE]\n\n');
824
+ } catch (e) {
825
+ logger.error(`Error sending error message: ${e}`);
826
+ } finally {
827
+ chunkQueue.end();
828
+ }
829
+ });
830
+ } catch (error) {
831
+ logger.error(`Notion API请求失败: ${error}`);
832
+ // 清理全局对象
833
+ try {
834
+ if (global.window) delete global.window;
835
+ if (global.document) delete global.document;
836
+
837
+ // 安全地删除navigator
838
+ if (global.navigator) {
839
+ try {
840
+ delete global.navigator;
841
+ } catch (navError) {
842
+ // 如果无法删除,尝试将其设置为undefined
843
+ try {
844
+ Object.defineProperty(global, 'navigator', {
845
+ value: undefined,
846
+ writable: true,
847
+ configurable: true
848
+ });
849
+ } catch (defineError) {
850
+ logger.warning(`无法清理navigator: ${defineError.message}`);
851
+ }
852
+ }
853
+ }
854
+ } catch (cleanupError) {
855
+ logger.warning(`清理全局对象时出错: ${cleanupError.message}`);
856
+ }
857
+
858
+ if (timeoutId) clearTimeout(timeoutId);
859
+ if (chunkQueue) chunkQueue.end();
860
+
861
+ // 确保在错误情况下也触发流结束
862
+ try {
863
+ if (!responseReceived && chunkQueue) {
864
+ const errorChunk = new ChatCompletionChunk({
865
+ choices: [
866
+ new Choice({
867
+ delta: new ChoiceDelta({ content: `Notion API请求失败: ${error.message}` }),
868
+ finish_reason: "error"
869
+ })
870
+ ]
871
+ });
872
+ chunkQueue.write(`data: ${JSON.stringify(errorChunk)}\n\n`);
873
+ chunkQueue.write('data: [DONE]\n\n');
874
+ }
875
+ } catch (e) {
876
+ logger.error(`发送错误消息时出错: ${e}`);
877
+ }
878
+
879
+ throw error; // 重新抛出错误以便上层捕获
880
+ }
881
+ }
882
+
883
+ // 应用初始化
884
+ async function initialize() {
885
+ logger.info(`初始化Notion配置...`);
886
+
887
+ // 启动代理服务器
888
+ try {
889
+ await proxyServer.start();
890
+ } catch (error) {
891
+ logger.error(`启动代理服务器失败: ${error.message}`);
892
+ }
893
+
894
+ // 检查是否已从环境变量中获取ID
895
+ if (NOTION_SPACE_ID && FETCHED_NOTION_USER_ID) {
896
+ logger.info(`使用环境变量中的Notion ID配置
897
+ - NOTION_SPACE_ID: ${NOTION_SPACE_ID}
898
+ - NOTION_ACTIVE_USER_HEADER: ${FETCHED_NOTION_USER_ID}`);
899
+ // logger.info(`- NOTION_SPACE_ID: ${NOTION_SPACE_ID}`);
900
+ // logger.info(`- NOTION_ACTIVE_USER_HEADER: ${FETCHED_NOTION_USER_ID}`);
901
+ FETCHED_IDS_SUCCESSFULLY = true;
902
+ } else {
903
+ // 尝试从Notion API获取ID
904
+ NOTION_COOKIE = getCookie(); // 获取第一个cookie
905
+ if (!NOTION_COOKIE) {
906
+ logger.error(`错误: 未设置NOTION_COOKIE环境变量,应用无法正常工作`);
907
+ logger.error(`请在.env文件中设置有效的NOTION_COOKIE值`);
908
+ return;
909
+ }
910
+
911
+ logger.info(`未检测到ID配置,正在通过API自动获取Notion ID...`);
912
+ await fetchAndSetNotionIds();
913
+
914
+ if (!FETCHED_IDS_SUCCESSFULLY) {
915
+ logger.error(`获取Notion ID失败,应用无法正常工作`);
916
+ logger.error(`请检查以下可能的问题:`);
917
+ logger.error(`1. NOTION_COOKIE是否有效`);
918
+ logger.error(`2. 网络连接是否正常`);
919
+ logger.error(`3. Notion服务是否可访问`);
920
+ logger.error(`或者手动在.env文件中设置NOTION_SPACE_ID和NOTION_ACTIVE_USER_HEADER`);
921
+ } else {
922
+ logger.info(`成功通过API获取Notion ID
923
+ - 用户ID: ${FETCHED_NOTION_USER_ID}
924
+ - 空间ID: ${NOTION_SPACE_ID}`);
925
+ // logger.info(`- 用户ID: ${FETCHED_NOTION_USER_ID}`);
926
+ // logger.info(`- 空间ID: ${NOTION_SPACE_ID}`);
927
+ }
928
+ }
929
+ if (process.env.USE_NATIVE_PROXY_POOL === 'true') {
930
+ logger.info(`正在初始化本地代理池...`);
931
+ await proxyPool.initialize();
932
+ }
933
+ }
934
+
935
+ // 导出函数
936
+ export {
937
+ initialize,
938
+ streamNotionResponse,
939
+ buildNotionRequest,
940
+ FETCHED_IDS_SUCCESSFULLY
941
+ };
src/models.js ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { randomUUID } from 'crypto';
2
+
3
+ // 输入模型 (OpenAI-like)
4
+ export class ChatMessage {
5
+ constructor({
6
+ id = generateCustomId(),
7
+ role,
8
+ content,
9
+ userId = null,
10
+ createdAt = null,
11
+ traceId = null
12
+ }) {
13
+ this.id = id;
14
+ this.role = role; // "system", "user", "assistant"
15
+ this.content = content;
16
+ this.userId = userId;
17
+ this.createdAt = createdAt;
18
+ this.traceId = traceId;
19
+ }
20
+ }
21
+
22
+ export class ChatCompletionRequest {
23
+ constructor({
24
+ messages,
25
+ model = "notion-proxy",
26
+ stream = false,
27
+ notion_model = "anthropic-opus-4"
28
+ }) {
29
+ this.messages = messages;
30
+ this.model = model;
31
+ this.stream = stream;
32
+ this.notion_model = notion_model;
33
+ }
34
+ }
35
+
36
+ // Notion 模型
37
+ export class NotionTranscriptConfigValue {
38
+ constructor({
39
+ type = "markdown-chat",
40
+ model
41
+ }) {
42
+ this.type = type;
43
+ this.model = model;
44
+ }
45
+ }
46
+
47
+
48
+ export class NotionTranscriptContextValue {
49
+ constructor({
50
+ userId,
51
+ spaceId,
52
+ surface = "home_module",
53
+ timezone = "America/Los_Angeles",
54
+ userName,
55
+ spaceName,
56
+ spaceViewId,
57
+ currentDatetime
58
+ }) {
59
+ this.userId = userId;
60
+ this.spaceId = spaceId;
61
+ this.surface = surface;
62
+ this.timezone = timezone;
63
+ this.userName = userName;
64
+ this.spaceName = spaceName;
65
+ this.spaceViewId = spaceViewId;
66
+ this.currentDatetime = currentDatetime;
67
+ }
68
+ }
69
+
70
+ export class NotionTranscriptItem {
71
+ constructor({
72
+ id = generateCustomId(),
73
+ type,
74
+ value = null,
75
+
76
+ }) {
77
+ this.id = id;
78
+ this.type = type; // "markdown-chat", "agent-integration", "context"
79
+ this.value = value;
80
+ }
81
+ }
82
+
83
+ export class NotionTranscriptItemByuser {
84
+ constructor({
85
+ id = generateCustomId(),
86
+ type,
87
+ value = null,
88
+ userId,
89
+ createdAt
90
+
91
+ }) {
92
+ this.id = id;
93
+ this.type = type; // "config", "user"
94
+ this.value = value;
95
+ this.userId = userId;
96
+ this.createdAt = createdAt;
97
+ }
98
+ }
99
+
100
+ export class NotionDebugOverrides {
101
+ constructor({
102
+ cachedInferences = {},
103
+ annotationInferences = {},
104
+ emitInferences = false
105
+ }) {
106
+ this.cachedInferences = cachedInferences;
107
+ this.annotationInferences = annotationInferences;
108
+ this.emitInferences = emitInferences;
109
+ }
110
+ }
111
+
112
+ export function generateCustomId() {
113
+ // 创建固定部分
114
+ const prefix1 = '2036702a';
115
+ const prefix2 = '4d19';
116
+ const prefix5 = '00aa';
117
+
118
+ // 生成随机十六进制字符
119
+ function randomHex(length) {
120
+ return Array(length).fill(0).map(() =>
121
+ Math.floor(Math.random() * 16).toString(16)
122
+ ).join('');
123
+ }
124
+
125
+ // 组合所有部分
126
+ const part3 = '80' + randomHex(2); // 8xxx
127
+ const part4 = randomHex(4); // xxxx
128
+ const part5 = prefix5 + randomHex(8); // 00aaxxxxxxxx
129
+
130
+ return `${prefix1}-${prefix2}-${part3}-${part4}-${part5}`;
131
+ }
132
+
133
+ export class NotionRequestBody {
134
+ constructor({
135
+ traceId = randomUUID(),
136
+ spaceId,
137
+ transcript,
138
+ createThread = false,
139
+ debugOverrides = new NotionDebugOverrides({}),
140
+ generateTitle = true,
141
+ saveAllThreadOperations = true,
142
+ }) {
143
+ this.traceId = traceId;
144
+ this.spaceId = spaceId;
145
+ this.transcript = transcript;
146
+ this.createThread = createThread;
147
+ this.debugOverrides = debugOverrides;
148
+ this.generateTitle = generateTitle;
149
+ this.saveAllThreadOperations = saveAllThreadOperations;
150
+ }
151
+ }
152
+
153
+ // 输出模型 (OpenAI SSE)
154
+ export class ChoiceDelta {
155
+ constructor({
156
+ content = null
157
+ }) {
158
+ this.content = content;
159
+ }
160
+ }
161
+
162
+ export class Choice {
163
+ constructor({
164
+ index = 0,
165
+ delta,
166
+ finish_reason = null
167
+ }) {
168
+ this.index = index;
169
+ this.delta = delta;
170
+ this.finish_reason = finish_reason;
171
+ }
172
+ }
173
+
174
+ export class ChatCompletionChunk {
175
+ constructor({
176
+ id = `chatcmpl-${randomUUID()}`,
177
+ object = "chat.completion.chunk",
178
+ created = Math.floor(Date.now() / 1000),
179
+ model = "notion-proxy",
180
+ choices
181
+ }) {
182
+ this.id = id;
183
+ this.object = object;
184
+ this.created = created;
185
+ this.model = model;
186
+ this.choices = choices;
187
+ }
188
+ }
189
+
190
+ // 模型列表端点 /v1/models
191
+ export class Model {
192
+ constructor({
193
+ id,
194
+ object = "model",
195
+ created = Math.floor(Date.now() / 1000),
196
+ owned_by = "notion"
197
+ }) {
198
+ this.id = id;
199
+ this.object = object;
200
+ this.created = created;
201
+ this.owned_by = owned_by;
202
+ }
203
+ }
204
+
205
+ export class ModelList {
206
+ constructor({
207
+ object = "list",
208
+ data
209
+ }) {
210
+ this.object = object;
211
+ this.data = data;
212
+ }
213
+ }
src/proxy/chrome_proxy_server_android_arm64 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f4855fc6df77b627a346d0a52beb158e0f6b35ab035bb72f80f7e0f090eb13e7
3
+ size 12777353
src/proxy/chrome_proxy_server_linux_amd64 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cfb52ae72313bbfaf22c430b7c180456fbb59ce2a84f8738a63cb7e3be0a8691
3
+ size 12762272
src/proxy/chrome_proxy_server_windows_amd64.exe ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:530200aa125db2ef7bc39bb852d0a41acf538757c29585ec7bde0530f1a31629
3
+ size 12875776