├── .gitignore
├── .env.example
├── .dockerignore
├── wrangler.toml
├── package.json
├── src
├── utils
│ ├── config.js
│ └── crypto.js
├── worker.js
└── client.js
├── .github
└── workflows
│ └── docker.yml
├── Dockerfile
├── CLAUDE.md
├── README.md
├── pnpm-lock.yaml
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | .wrangler
4 | dist
5 | .dev
6 | *.log
7 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # 客户端配置
2 | URL="ws://127.0.0.1:8787/ws"
3 | KEY="wsdog"
4 | METHOD="aes-256-gcm"
5 | TIMEOUT=600
6 |
7 | LOCAL_HOST=127.0.0.1
8 | LOCAL_PORT=1080
9 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .git
3 | .gitignore
4 | .env
5 | .env.*
6 | *.md
7 | wrangler.toml
8 | .wrangler
9 | dist
10 | .DS_Store
11 | npm-debug.log
12 | yarn-error.log
13 | pnpm-debug.log
14 |
--------------------------------------------------------------------------------
/wrangler.toml:
--------------------------------------------------------------------------------
1 | name = "wsdog"
2 | main = "src/worker.js"
3 | compatibility_date = "2024-10-01"
4 |
5 | # 环境变量配置
6 | [vars]
7 | KEY = "wsdog"
8 | METHOD = "aes-256-gcm"
9 |
10 | # 生产环境配置
11 | [env.production]
12 | name = "wsdog-production"
13 | vars = { KEY = "your-production-key", METHOD = "aes-256-gcm" }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wsdog",
3 | "version": "2.0.0",
4 | "type": "module",
5 | "description": "An encrypted proxy service program through websocket",
6 | "repository": "git@github.com:metowolf/wsdog.git",
7 | "author": "metowolf ",
8 | "license": "MIT",
9 | "scripts": {
10 | "dev": "wrangler dev src/worker.js",
11 | "deploy": "wrangler deploy src/worker.js",
12 | "client": "node src/client.js"
13 | },
14 | "dependencies": {
15 | "dotenv": "^16.4.5",
16 | "hono": "^4.6.14",
17 | "ws": "^8.18.0"
18 | },
19 | "devDependencies": {
20 | "wrangler": "^4.42.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/utils/config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 配置管理模块 - ESM 版本
3 | */
4 |
5 | import { config as dotenvConfig } from 'dotenv'
6 | import { fileURLToPath } from 'url'
7 | import { dirname, join } from 'path'
8 |
9 | const __filename = fileURLToPath(import.meta.url)
10 | const __dirname = dirname(__filename)
11 |
12 | // 加载 .env 文件
13 | dotenvConfig({ path: join(__dirname, '../../.env') })
14 |
15 | export const config = {
16 | url: process.env.URL || 'ws://127.0.0.1:8787/ws',
17 | key: process.env.KEY || 'wsdog',
18 | method: process.env.METHOD || 'aes-256-gcm',
19 | timeout: parseInt(process.env.TIMEOUT || '600', 10) * 1000,
20 | local_host: process.env.LOCAL_HOST || '127.0.0.1',
21 | local_port: process.env.LOCAL_PORT || '1080',
22 | }
23 |
--------------------------------------------------------------------------------
/.github/workflows/docker.yml:
--------------------------------------------------------------------------------
1 | name: Build and Push Docker Image
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | tags:
8 | - 'v*'
9 |
10 | env:
11 | REGISTRY: ghcr.io
12 | IMAGE_NAME: ${{ github.repository }}
13 |
14 | jobs:
15 | build-and-push:
16 | runs-on: ubuntu-latest
17 | permissions:
18 | contents: read
19 | packages: write
20 |
21 | steps:
22 | - name: Checkout repository
23 | uses: actions/checkout@v4
24 |
25 | - name: Set up Docker Buildx
26 | uses: docker/setup-buildx-action@v3
27 |
28 | - name: Log in to GitHub Container Registry
29 | uses: docker/login-action@v3
30 | with:
31 | registry: ${{ env.REGISTRY }}
32 | username: ${{ github.actor }}
33 | password: ${{ secrets.GITHUB_TOKEN }}
34 |
35 | - name: Extract metadata for Docker
36 | id: meta
37 | uses: docker/metadata-action@v5
38 | with:
39 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
40 | tags: |
41 | type=ref,event=branch
42 | type=semver,pattern={{version}}
43 | type=semver,pattern={{major}}.{{minor}}
44 | type=semver,pattern={{major}}
45 | type=raw,value=latest,enable={{is_default_branch}}
46 |
47 | - name: Build and push Docker image
48 | uses: docker/build-push-action@v5
49 | with:
50 | context: .
51 | platforms: linux/amd64,linux/arm64
52 | push: true
53 | tags: ${{ steps.meta.outputs.tags }}
54 | labels: ${{ steps.meta.outputs.labels }}
55 | cache-from: type=gha
56 | cache-to: type=gha,mode=max
57 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:20-alpine
2 |
3 | WORKDIR /app
4 |
5 | # 复制依赖配置
6 | COPY package.json ./
7 | COPY pnpm-lock.yaml* ./
8 |
9 | # 安装 pnpm 和依赖
10 | RUN corepack enable && \
11 | corepack prepare pnpm@latest --activate && \
12 | pnpm install --prod
13 |
14 | # 复制源代码
15 | COPY src ./src
16 |
17 | # 设置环境变量默认值
18 | ENV NODE_ENV=production \
19 | LOCAL_HOST=0.0.0.0 \
20 | LOCAL_PORT=1080 \
21 | TIMEOUT=600
22 |
23 | # 暴露 SOCKS5 端口
24 | EXPOSE 1080
25 |
26 | # 启动脚本
27 | COPY <<'EOF' /app/entrypoint.sh
28 | #!/bin/sh
29 | set -e
30 |
31 | case "$1" in
32 | client)
33 | # 检查必需的环境变量
34 | if [ -z "$URL" ]; then
35 | echo "Error: URL environment variable is required for client mode"
36 | exit 1
37 | fi
38 | if [ -z "$KEY" ]; then
39 | echo "Error: KEY environment variable is required for client mode"
40 | exit 1
41 | fi
42 |
43 | echo "Starting wsdog client..."
44 | echo "Connecting to: $URL"
45 | echo "Local proxy: ${LOCAL_HOST}:${LOCAL_PORT}"
46 | exec node src/client.js
47 | ;;
48 | server)
49 | echo "Error: Server mode requires Cloudflare Workers deployment"
50 | echo "Please use 'pnpm run deploy' to deploy to Cloudflare Workers"
51 | exit 1
52 | ;;
53 | *)
54 | echo "Usage: docker run [options] wsdog [client|server]"
55 | echo ""
56 | echo "Commands:"
57 | echo " client Start SOCKS5 client (requires URL and KEY env vars)"
58 | echo " server Not supported - use Cloudflare Workers deployment"
59 | echo ""
60 | echo "Required environment variables for client mode:"
61 | echo " URL WebSocket server URL (e.g., ws://example.com/ws)"
62 | echo " KEY Encryption key (must match server)"
63 | echo ""
64 | echo "Optional environment variables:"
65 | echo " METHOD Encryption method (default: aes-256-gcm)"
66 | echo " LOCAL_HOST Local bind address (default: 0.0.0.0)"
67 | echo " LOCAL_PORT Local SOCKS5 port (default: 1080)"
68 | echo " TIMEOUT Connection timeout in seconds (default: 600)"
69 | echo ""
70 | echo "Example:"
71 | echo " docker run -e URL=ws://example.com/ws -e KEY=mysecret -p 1080:1080 wsdog client"
72 | exit 1
73 | ;;
74 | esac
75 | EOF
76 |
77 | RUN chmod +x /app/entrypoint.sh
78 |
79 | ENTRYPOINT ["/app/entrypoint.sh"]
80 | CMD ["client"]
81 |
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## 项目概述
6 |
7 | wsdog 是一个通过 WebSocket 实现的加密代理服务程序,使用 ESM 标准重构,基于 Hono 框架。支持部署到 Cloudflare Workers。
8 |
9 | **架构组成**:
10 | - **服务端 (src/worker.js)**: 基于 Hono 的 WebSocket 服务器,部署在 Cloudflare Workers,接收客户端连接并转发到目标地址
11 | - **客户端 (src/client.js)**: SOCKS5 代理服务器,运行在本地 Node.js 环境,将本地请求通过 WebSocket 转发到服务端
12 |
13 | ## 核心架构
14 |
15 | ### 连接流程
16 | 1. 客户端启动 SOCKS5 服务器 (默认 127.0.0.1:1080)
17 | 2. 接收 SOCKS5 连接后,客户端建立 WebSocket 连接到服务端
18 | 3. 使用 AES-GCM 加密算法对传输数据进行加密
19 | 4. 服务端解密后使用 Cloudflare Workers 的 `connect()` API 连接到目标地址
20 | 5. 建立双向数据转发通道
21 |
22 | ### 状态机设计
23 |
24 | **服务端三阶段** (src/worker.js):
25 | - `STAGE_CONNECT (0x01)`: 接收连接请求,解析目标地址和端口,建立到目标的 TCP 连接
26 | - `STAGE_CACHE (0x10)`: 缓存客户端数据,等待远程连接建立完成
27 | - `STAGE_PIPE (0x11)`: 双向转发加密数据 (WebSocket ↔ TCP)
28 |
29 | **客户端四阶段** (src/client.js):
30 | - `STAGE_INIT (0x00)`: SOCKS5 握手,响应 `0x05 0x00` (无需认证)
31 | - `STAGE_CONNECT (0x01)`: 解析 SOCKS5 请求,建立 WebSocket 连接,发送加密的目标地址
32 | - `STAGE_CACHE (0x10)`: 缓存本地数据,等待 WebSocket 连接建立
33 | - `STAGE_PIPE (0x11)`: 双向转发加密数据 (SOCKS5 ↔ WebSocket)
34 |
35 | ### 加密机制
36 |
37 | 使用两套独立的加密实现,保持协议兼容:
38 |
39 | **服务端 (src/utils/crypto.js)**:
40 | - 使用 Web Crypto API (Cloudflare Workers 兼容)
41 | - PBKDF2 密钥派生: `password + "salt" + 10000 iterations + SHA-256`
42 | - AES-GCM 加密,128-bit 认证标签
43 |
44 | **客户端 (src/utils/crypto-node.js)**:
45 | - 使用 Node.js crypto 模块
46 | - 相同的 PBKDF2 参数保持兼容性
47 | - AES-GCM 加密,认证标签附加在密文末尾 (最后 16 字节)
48 |
49 | **会话加密流程**:
50 | 1. 客户端首次连接时生成 16 字节随机 nonce
51 | 2. 首个消息: `encrypt(addr + port, nonce) + nonce` (共享 nonce)
52 | 3. 后续消息: `encrypt(data, nonce)` (使用相同 nonce)
53 | 4. 服务端使用接收到的 nonce 解密所有后续数据
54 |
55 | ## 开发命令
56 |
57 | ### 依赖管理
58 | ```bash
59 | pnpm install # 安装依赖
60 | ```
61 |
62 | ### 本地开发
63 | ```bash
64 | pnpm run dev # 启动 Wrangler 本地开发服务器 (默认 http://127.0.0.1:8787)
65 | pnpm run client # 启动 SOCKS5 客户端 (需要先配置 .env)
66 | ```
67 |
68 | ### 部署
69 | ```bash
70 | pnpm run deploy # 部署到 Cloudflare Workers
71 | ```
72 |
73 | ### 测试
74 | ```bash
75 | curl -Lx socks5h://127.0.0.1:1080 www.google.com
76 | ```
77 |
78 | ## 配置说明
79 |
80 | ### 服务端配置 (wrangler.toml)
81 | ```toml
82 | [vars]
83 | KEY = "wsdog" # 加密密钥
84 | METHOD = "aes-256-gcm" # 加密算法
85 | ```
86 |
87 | ### 客户端配置 (.env)
88 | ```env
89 | URL="ws://127.0.0.1:8787/ws" # WebSocket 服务器地址
90 | KEY="wsdog" # 加密密钥 (需与服务端一致)
91 | METHOD="aes-256-gcm" # 加密算法 (需与服务端一致)
92 | TIMEOUT=600 # 连接超时 (秒)
93 | LOCAL_HOST=127.0.0.1 # 本地监听地址
94 | LOCAL_PORT=1080 # SOCKS5 端口
95 | ```
96 |
97 | **支持的加密算法**: `aes-128-gcm`, `aes-192-gcm`, `aes-256-gcm`, `none` (测试用)
98 |
99 | ## 代码规范
100 |
101 | - 使用 ESM 模块标准 (`import`/`export`)
102 | - 服务端仅使用 Web 标准 API (Web Crypto API, WebSocket API, Cloudflare Workers API)
103 | - 客户端使用 Node.js 原生模块 (net, crypto, ws)
104 | - 异步操作优先使用 `async/await`
105 | - 状态机模式管理连接生命周期
106 | - 控制台日志使用 `[模块名]` 前缀格式
107 | - 所有二进制数据使用 `Uint8Array` 或 `Buffer` 处理
108 |
109 | ## Cloudflare Workers 约束
110 |
111 | - 不支持原生 TCP socket,必须使用 `connect()` API 建立 TCP 连接
112 | - WebSocket 连接通过 `WebSocketPair` 创建
113 | - CPU 时间限制 (免费版 10ms,付费版 50ms)
114 | - 内存限制 (128MB)
115 | - 请求超时 (免费版 30s,付费版可配置)
116 | - 不支持 Node.js 特定 API (fs, path, child_process 等)
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | An encrypted proxy service program through websocket.
8 |
9 |
10 | Project Source ·
11 | Telegram Channel
12 |
13 |
14 | ---
15 |
16 | ## wsdog v2.0 - Hono + Cloudflare Workers
17 |
18 | 基于 Hono 框架重构的 wsdog,支持部署到 Cloudflare Workers。
19 |
20 | ### 主要特性
21 |
22 | - ✅ 使用 **ESM** 标准重构全部代码
23 | - ✅ 服务端基于 **Hono** 框架,支持部署到 **Cloudflare Workers**
24 | - ✅ 使用 **Web Crypto API** 实现加密 (兼容 Workers 环境)
25 | - ✅ 简化配置,使用 **wrangler.toml** 管理服务端配置
26 | - ✅ 保持完整的加密和 SOCKS5 代理功能
27 | - ✅ 使用 **pnpm** 作为包管理器
28 |
29 | ## 快速开始
30 |
31 | ### 1. 安装依赖
32 |
33 | ```bash
34 | pnpm install
35 | ```
36 |
37 | ### 2. 本地开发
38 |
39 | 启动服务端 (使用 Wrangler 本地开发服务器):
40 | ```bash
41 | pnpm run dev
42 | ```
43 |
44 | 在另一个终端启动客户端:
45 | ```bash
46 | # 创建 .env 文件
47 | cp .env.example .env
48 |
49 | # 编辑 .env,设置 URL 和密钥
50 | # URL="ws://127.0.0.1:8787/ws"
51 | # KEY="your-secret-key"
52 |
53 | pnpm run client
54 | ```
55 |
56 | ### 3. 测试连接
57 |
58 | ```bash
59 | curl -Lx socks5h://127.0.0.1:1080 www.google.com
60 | ```
61 |
62 | ### 4. 部署到 Cloudflare Workers
63 |
64 | 编辑 `wrangler.toml`,设置生产环境配置:
65 | ```toml
66 | [env.production]
67 | name = "wsdog-production"
68 | vars = { KEY = "your-production-key", METHOD = "aes-256-gcm" }
69 | ```
70 |
71 | 部署:
72 | ```bash
73 | pnpm run deploy
74 | ```
75 |
76 | 部署后更新客户端 `.env`:
77 | ```env
78 | URL="wss://wsdog-production.your-account.workers.dev/ws"
79 | KEY="your-production-key"
80 | METHOD="aes-256-gcm"
81 | ```
82 |
83 | ## 配置说明
84 |
85 | ### 服务端配置 (wrangler.toml)
86 |
87 | ```toml
88 | [vars]
89 | KEY = "wsdog" # 加密密钥
90 | METHOD = "aes-256-gcm" # 加密算法
91 | ```
92 |
93 | ### 客户端配置 (.env)
94 |
95 | ```env
96 | URL="ws://127.0.0.1:8787/ws" # WebSocket 服务器地址
97 | KEY="wsdog" # 加密密钥 (需与服务端一致)
98 | METHOD="aes-256-gcm" # 加密算法 (需与服务端一致)
99 | TIMEOUT=600 # 超时时间 (秒)
100 | LOCAL_HOST=127.0.0.1 # 本地监听地址
101 | LOCAL_PORT=1080 # SOCKS5 端口
102 | ```
103 |
104 | ### 支持的加密算法
105 |
106 | - `aes-128-gcm`
107 | - `aes-192-gcm`
108 | - `aes-256-gcm`
109 | - `none` (仅用于测试,不推荐生产环境)
110 |
111 | ## 架构说明
112 |
113 | ### 组件
114 |
115 | - **服务端 (src/worker.js)**: 基于 Hono 的 WebSocket 服务器,部署在 Cloudflare Workers
116 | - **客户端 (src/client.js)**: SOCKS5 代理服务器,运行在本地 Node.js 环境
117 |
118 | ### 项目结构
119 |
120 | ```
121 | wsdog/
122 | ├── src/
123 | │ ├── worker.js # Cloudflare Workers 入口 (Hono 应用)
124 | │ ├── client.js # SOCKS5 客户端
125 | │ └── utils/
126 | │ ├── crypto.js # Web Crypto API 加密 (服务端)
127 | │ ├── crypto-node.js # Node.js 加密 (客户端)
128 | │ └── config.js # 客户端配置管理
129 | ├── wrangler.toml # Cloudflare Workers 配置
130 | ├── package.json # 项目依赖和脚本
131 | └── .env.example # 客户端配置示例
132 | ```
133 |
134 | ## 优势
135 |
136 | ### Cloudflare Workers 部署
137 |
138 | - ✅ **全球边缘网络**: 自动部署到全球数据中心
139 | - ✅ **高可用**: 无需管理服务器
140 | - ✅ **快速**: 边缘计算,低延迟
141 | - ✅ **免费额度**: 每天 100,000 次请求
142 |
143 | ### 技术栈
144 |
145 | - ✅ **Hono**: 轻量级、高性能 Web 框架
146 | - ✅ **ESM**: 现代 JavaScript 模块标准
147 | - ✅ **Web Crypto API**: 标准化加密接口
148 | - ✅ **pnpm**: 快速、节省磁盘空间的包管理器
149 |
150 | ## 常见问题
151 |
152 | ### Q: 如何调试?
153 |
154 | A: 使用 `pnpm run dev` 启动本地开发模式,支持热重载和日志输出。
155 |
156 | ### Q: 为什么客户端还是 Node.js?
157 |
158 | A: SOCKS5 代理需要监听本地端口,这个功能 Cloudflare Workers 不支持,必须在本地运行。
159 |
160 | ### Q: v1.0 和 v2.0 能否互通?
161 |
162 | A: 不能。v2.0 使用 PBKDF2 密钥派生,与 v1.0 的实现不兼容。
163 |
164 | ## 许可证
165 |
166 | MIT License
167 |
--------------------------------------------------------------------------------
/src/worker.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Cloudflare Workers 服务端 - 使用 Hono 框架
3 | */
4 |
5 | import { Hono } from 'hono'
6 | import { Crypto } from './utils/crypto.js'
7 | import { connect } from 'cloudflare:sockets'
8 |
9 | const app = new Hono()
10 |
11 | // 连接阶段常量
12 | const STAGE_CONNECT = 0x01
13 | const STAGE_CACHE = 0x10
14 | const STAGE_PIPE = 0x11
15 |
16 | /**
17 | * 处理 WebSocket 连接
18 | */
19 | async function handleWebSocket(request, env) {
20 | const upgradeHeader = request.headers.get('Upgrade')
21 | if (!upgradeHeader || upgradeHeader !== 'websocket') {
22 | return new Response('Expected Upgrade: websocket', { status: 426 })
23 | }
24 |
25 | const webSocketPair = new WebSocketPair()
26 | const [client, server] = Object.values(webSocketPair)
27 |
28 | // 获取配置
29 | const key = env.KEY || 'wsdog'
30 | const method = env.METHOD || 'aes-256-gcm'
31 | const crypto = new Crypto(key, method)
32 |
33 | // 连接状态
34 | const state = {
35 | stage: STAGE_CONNECT,
36 | nonce: null,
37 | remote: null,
38 | cache: []
39 | }
40 |
41 | server.accept()
42 |
43 | server.addEventListener('message', async (event) => {
44 | try {
45 | // 兼容不同环境的 event.data 类型
46 | let arrayBuffer
47 | if (event.data instanceof ArrayBuffer) {
48 | arrayBuffer = event.data
49 | } else if (event.data.arrayBuffer) {
50 | arrayBuffer = await event.data.arrayBuffer()
51 | } else {
52 | throw new Error('Unsupported data type')
53 | }
54 | const data = new Uint8Array(arrayBuffer)
55 |
56 | switch (state.stage) {
57 | case STAGE_CONNECT:
58 | await handleConnect(state, data, crypto, server)
59 | break
60 | case STAGE_CACHE:
61 | await handleCache(state, data, crypto)
62 | break
63 | case STAGE_PIPE:
64 | await handlePipe(state, data, crypto)
65 | break
66 | }
67 | } catch (error) {
68 | console.error('[websocket] error:', error.message)
69 | server.close()
70 | }
71 | })
72 |
73 | server.addEventListener('close', () => {
74 | console.log('[websocket] closed')
75 | if (state.remote) {
76 | state.remote.close()
77 | }
78 | })
79 |
80 | return new Response(null, {
81 | status: 101,
82 | webSocket: client
83 | })
84 | }
85 |
86 | /**
87 | * 连接阶段 - 解析目标地址并建立连接
88 | */
89 | async function handleConnect(state, data, crypto, server) {
90 | // 提取 nonce (最后 16 字节)
91 | state.nonce = data.slice(-16)
92 |
93 | // 解密数据
94 | const decrypted = await crypto.decrypt(data.slice(0, -16), state.nonce)
95 | if (decrypted === null) {
96 | console.error('[connect] decryption failed')
97 | server.close()
98 | return
99 | }
100 |
101 | state.stage = STAGE_CACHE
102 |
103 | // 解析地址和端口
104 | const addr = new TextDecoder().decode(decrypted.slice(0, -2))
105 | const port = new DataView(decrypted.buffer, decrypted.byteOffset + decrypted.length - 2, 2).getUint16(0)
106 |
107 | console.log(`[connect] ${addr}:${port}`)
108 |
109 | try {
110 | // 在 Cloudflare Workers 中连接到远程主机
111 | state.remote = connect({ hostname: addr, port })
112 |
113 | // 读取远程数据
114 | const reader = state.remote.readable.getReader()
115 | const writer = state.remote.writable.getWriter()
116 |
117 | // 发送缓存的数据
118 | for (const buf of state.cache) {
119 | await writer.write(buf)
120 | }
121 | state.cache = []
122 | state.stage = STAGE_PIPE
123 |
124 | // 转发远程数据到 WebSocket
125 | ;(async () => {
126 | try {
127 | while (true) {
128 | const { done, value } = await reader.read()
129 | if (done) break
130 |
131 | const encrypted = await crypto.encrypt(value, state.nonce)
132 | server.send(encrypted)
133 | }
134 | } catch (error) {
135 | console.error('[remote] error:', error.message)
136 | } finally {
137 | server.close()
138 | }
139 | })()
140 |
141 | // 保存 writer 供后续使用
142 | state.writer = writer
143 |
144 | } catch (error) {
145 | console.error('[connect] error:', error.message)
146 | server.close()
147 | }
148 | }
149 |
150 | /**
151 | * 缓存阶段 - 缓存数据等待连接建立
152 | */
153 | async function handleCache(state, data, crypto) {
154 | const decrypted = await crypto.decrypt(data, state.nonce)
155 | if (decrypted !== null) {
156 | state.cache.push(decrypted)
157 | }
158 | }
159 |
160 | /**
161 | * 转发阶段 - 直接转发数据
162 | */
163 | async function handlePipe(state, data, crypto) {
164 | const decrypted = await crypto.decrypt(data, state.nonce)
165 | if (decrypted !== null && state.writer) {
166 | await state.writer.write(decrypted)
167 | }
168 | }
169 |
170 | /**
171 | * 主页处理 - 返回 404 模拟 nginx
172 | */
173 | app.get('/', (c) => {
174 | return c.html(
175 | '404 Not Found404 Not Found
nginx',
176 | 404
177 | )
178 | })
179 |
180 | /**
181 | * WebSocket 路由
182 | */
183 | app.get('/ws', async (c) => {
184 | return handleWebSocket(c.req.raw, c.env)
185 | })
186 |
187 | export default app
188 |
--------------------------------------------------------------------------------
/src/client.js:
--------------------------------------------------------------------------------
1 | /**
2 | * SOCKS5 客户端 - ESM 版本
3 | */
4 |
5 | import net from 'net'
6 | import WebSocket from 'ws'
7 | import { config } from './utils/config.js'
8 | import { Crypto } from './utils/crypto.js'
9 |
10 | const STAGE_INIT = 0x00
11 | const STAGE_CONNECT = 0x01
12 | const STAGE_CACHE = 0x10
13 | const STAGE_PIPE = 0x11
14 |
15 | const SOCKS_VERSION = 0x05
16 | const NO_AUTH = 0x00
17 |
18 | const COMMAND_CONNECT = 0x01
19 | const COMMAND_BIND = 0x02
20 | const COMMAND_UDP = 0x03
21 |
22 | const RESERVED = 0x00
23 |
24 | const ATYP_IPV4 = 0x01
25 | const ATYP_DOMAIN = 0x03
26 | const ATYP_IPV6 = 0x04
27 |
28 | const REPLY_SUCCEEDED = 0x00
29 | const REPLY_COMMAND_NOT_SUPPORTED = 0x07
30 | const REPLY_ATYP_NOT_SUPPORTED = 0x08
31 |
32 | // 创建加密器
33 | const crypto = new Crypto(config.key, config.method)
34 |
35 | /**
36 | * 初始化阶段
37 | */
38 | function stageInit(info) {
39 | const buf = Buffer.from([SOCKS_VERSION, NO_AUTH])
40 | info.socket.write(buf)
41 | info.stage = STAGE_CONNECT
42 | }
43 |
44 | /**
45 | * 连接阶段
46 | */
47 | function stageConnect(info, data) {
48 | const cmd = data[1]
49 | if (![COMMAND_CONNECT].includes(cmd)) {
50 | const buf = Buffer.from([SOCKS_VERSION, REPLY_COMMAND_NOT_SUPPORTED, RESERVED, ATYP_IPV4])
51 | info.socket.end(buf)
52 | return
53 | }
54 |
55 | const atype = data[3]
56 | if (![ATYP_IPV4, ATYP_DOMAIN, ATYP_IPV6].includes(atype)) {
57 | const buf = Buffer.from([SOCKS_VERSION, REPLY_ATYP_NOT_SUPPORTED, RESERVED, ATYP_IPV4])
58 | info.socket.end(buf)
59 | return
60 | }
61 |
62 | let addr, port
63 | if (atype === ATYP_IPV4) {
64 | addr = data.slice(4, 8).join('.')
65 | port = data.slice(8, 10)
66 | } else if (atype === ATYP_DOMAIN) {
67 | const len = data[4]
68 | addr = data.slice(5, 5 + len).toString()
69 | port = data.slice(5 + len, 5 + len + 2)
70 | } else if (atype === ATYP_IPV6) {
71 | addr = Array.from(data.slice(4, 20))
72 | .map((b, i) => i % 2 === 0 ? b.toString(16).padStart(2, '0') : b.toString(16).padStart(2, '0'))
73 | .join('')
74 | .match(/.{1,4}/g)
75 | .join(':')
76 | port = data.slice(20, 22)
77 | }
78 |
79 | info.stage = STAGE_CACHE
80 |
81 | const buf = Buffer.from([SOCKS_VERSION, REPLY_SUCCEEDED, RESERVED, ATYP_IPV4, RESERVED, RESERVED, RESERVED, RESERVED])
82 | const reply = Buffer.concat([buf, port])
83 | info.socket.write(reply)
84 |
85 | // 建立 WebSocket 连接
86 | info.ws = new WebSocket(config.url)
87 |
88 | info.ws.on('open', () => {
89 | console.log('[ws] open')
90 |
91 | info.nonce = crypto.nonce()
92 |
93 | // 发送连接信息
94 | const addrBuf = Buffer.from(addr)
95 | const connectData = Buffer.concat([addrBuf, port])
96 | const encrypted = crypto.encrypt(connectData, info.nonce)
97 | const message = Buffer.concat([encrypted, info.nonce])
98 | info.ws.send(message)
99 |
100 | // 发送缓存的数据
101 | while (info.cache.length) {
102 | const cachedData = info.cache.shift()
103 | const encryptedData = crypto.encrypt(cachedData, info.nonce)
104 | info.ws.send(encryptedData)
105 | }
106 |
107 | info.stage = STAGE_PIPE
108 | info.heart = setInterval(() => {
109 | if (info.ws.readyState === WebSocket.OPEN) {
110 | info.ws.ping()
111 | }
112 | }, 30 * 1000)
113 | })
114 |
115 | info.ws.on('message', (data) => {
116 | const decrypted = crypto.decrypt(data, info.nonce)
117 | if (decrypted !== null) {
118 | info.socket.write(decrypted)
119 | }
120 | })
121 |
122 | info.ws.on('close', () => {
123 | console.log('[ws] closed')
124 | clearInterval(info.heart)
125 | info.socket.end()
126 | })
127 |
128 | info.ws.on('error', (error) => {
129 | console.log('[ws] error:', error.message)
130 | info.socket.destroy()
131 | })
132 | }
133 |
134 | /**
135 | * 缓存阶段
136 | */
137 | function stageCache(info, data) {
138 | info.cache.push(data)
139 | }
140 |
141 | /**
142 | * 转发阶段
143 | */
144 | function stagePipe(info, data) {
145 | const encrypted = crypto.encrypt(data, info.nonce)
146 | if (info.ws.readyState === WebSocket.OPEN) {
147 | info.ws.send(encrypted)
148 | }
149 | }
150 |
151 | /**
152 | * 处理客户端连接
153 | */
154 | function handleClient(socket) {
155 | console.log('[client] connected')
156 |
157 | const info = {
158 | socket,
159 | nonce: null,
160 | ws: null,
161 | heart: null,
162 | stage: STAGE_INIT,
163 | cache: []
164 | }
165 |
166 | socket.on('data', (data) => {
167 | switch (info.stage) {
168 | case STAGE_INIT:
169 | return stageInit(info, data)
170 | case STAGE_CONNECT:
171 | return stageConnect(info, data)
172 | case STAGE_CACHE:
173 | return stageCache(info, data)
174 | case STAGE_PIPE:
175 | return stagePipe(info, data)
176 | }
177 | })
178 |
179 | socket.on('end', () => {
180 | console.log('[client] disconnected')
181 | if (info.ws) {
182 | info.ws.terminate()
183 | }
184 | })
185 |
186 | socket.on('error', (error) => {
187 | console.log('[client] error:', error.message)
188 | info.socket.destroy()
189 | if (info.ws) {
190 | info.ws.terminate()
191 | }
192 | })
193 |
194 | socket.setTimeout(config.timeout, () => {
195 | console.log('[client] timeout')
196 | info.socket.destroy()
197 | if (info.ws) {
198 | info.ws.terminate()
199 | }
200 | })
201 | }
202 |
203 | /**
204 | * 启动 SOCKS5 服务器
205 | */
206 | const server = net.createServer(handleClient)
207 |
208 | server.on('listening', () => {
209 | const info = server.address()
210 | console.log(`[server] listening at ${info.address}:${info.port}`)
211 | console.log(`[server] connecting to ${config.url}`)
212 | })
213 |
214 | server.on('error', (error) => {
215 | console.log('[server] error:', error.message)
216 | })
217 |
218 | server.listen(config.local_port, config.local_host)
219 |
--------------------------------------------------------------------------------
/src/utils/crypto.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 加密工具模块 - 支持 Cloudflare Workers 和 Node.js
3 | */
4 |
5 | const ALGORITHM_KEYLEN = {
6 | 'aes-128-gcm': 16,
7 | 'aes-192-gcm': 24,
8 | 'aes-256-gcm': 32,
9 | 'none': 1,
10 | }
11 |
12 | const PBKDF2_ITERATIONS = 10000
13 | const PBKDF2_DIGEST = 'sha256'
14 | const PBKDF2_SALT = 'salt'
15 |
16 | const WEB_CRYPTO_AVAILABLE = typeof globalThis.crypto?.subtle?.importKey === 'function'
17 |
18 | let nodeCrypto = null
19 |
20 | if (!WEB_CRYPTO_AVAILABLE && typeof process !== 'undefined' && process.versions?.node) {
21 | try {
22 | nodeCrypto = await import('node:crypto')
23 | if (nodeCrypto?.default && !nodeCrypto.pbkdf2Sync) {
24 | nodeCrypto = nodeCrypto.default
25 | }
26 | } catch {
27 | nodeCrypto = null
28 | }
29 | }
30 |
31 | function getNodeCrypto() {
32 | if (!nodeCrypto) {
33 | throw new Error('Node.js crypto module is not available in this environment')
34 | }
35 |
36 | return nodeCrypto
37 | }
38 |
39 | async function deriveKeyWeb(password, algorithm) {
40 | if (algorithm === 'none') {
41 | return new Uint8Array(1)
42 | }
43 |
44 | const keylen = ALGORITHM_KEYLEN[algorithm]
45 | const encoder = new TextEncoder()
46 | const passwordBuffer = encoder.encode(password)
47 | const saltBuffer = encoder.encode(PBKDF2_SALT)
48 |
49 | const baseKey = await crypto.subtle.importKey(
50 | 'raw',
51 | passwordBuffer,
52 | { name: 'PBKDF2' },
53 | false,
54 | ['deriveBits']
55 | )
56 |
57 | const derivedBits = await crypto.subtle.deriveBits(
58 | {
59 | name: 'PBKDF2',
60 | salt: saltBuffer,
61 | iterations: PBKDF2_ITERATIONS,
62 | hash: 'SHA-256'
63 | },
64 | baseKey,
65 | keylen * 8
66 | )
67 |
68 | return new Uint8Array(derivedBits)
69 | }
70 |
71 | function deriveKeyNode(password, algorithm) {
72 | if (algorithm === 'none') {
73 | return new Uint8Array(1)
74 | }
75 |
76 | const keylen = ALGORITHM_KEYLEN[algorithm]
77 | const { pbkdf2Sync } = getNodeCrypto()
78 | const keyBuffer = pbkdf2Sync(password, PBKDF2_SALT, PBKDF2_ITERATIONS, keylen, PBKDF2_DIGEST)
79 | return new Uint8Array(keyBuffer.buffer, keyBuffer.byteOffset, keyBuffer.byteLength)
80 | }
81 |
82 | function ensureUint8Array(data) {
83 | if (data instanceof Uint8Array) {
84 | return data
85 | }
86 | return new Uint8Array(data)
87 | }
88 |
89 | function ensureBuffer(data) {
90 | if (Buffer.isBuffer(data)) {
91 | return data
92 | }
93 | if (data instanceof Uint8Array) {
94 | return Buffer.from(data.buffer, data.byteOffset, data.byteLength)
95 | }
96 | return Buffer.from(data)
97 | }
98 |
99 | function encryptNode(algorithm, key, data, nonce) {
100 | if (algorithm === 'none') {
101 | return ensureBuffer(data)
102 | }
103 |
104 | const { createCipheriv } = getNodeCrypto()
105 | const keyBuffer = ensureBuffer(key)
106 | const iv = ensureBuffer(nonce)
107 | const dataBuffer = ensureBuffer(data)
108 |
109 | const cipher = createCipheriv(algorithm, keyBuffer, iv)
110 | const encrypted = Buffer.concat([cipher.update(dataBuffer), cipher.final()])
111 | const tag = cipher.getAuthTag()
112 | return Buffer.concat([encrypted, tag])
113 | }
114 |
115 | function decryptNode(algorithm, key, data, nonce) {
116 | if (algorithm === 'none') {
117 | return ensureBuffer(data)
118 | }
119 |
120 | try {
121 | const { createDecipheriv } = getNodeCrypto()
122 | const keyBuffer = ensureBuffer(key)
123 | const iv = ensureBuffer(nonce)
124 | const dataBuffer = ensureBuffer(data)
125 |
126 | const payload = dataBuffer.slice(0, -16)
127 | const tag = dataBuffer.slice(-16)
128 | const decipher = createDecipheriv(algorithm, keyBuffer, iv)
129 | decipher.setAuthTag(tag)
130 | const decrypted = Buffer.concat([decipher.update(payload), decipher.final()])
131 | return decrypted
132 | } catch (error) {
133 | return null
134 | }
135 | }
136 |
137 | /**
138 | * 创建加密器类
139 | */
140 | export class Crypto {
141 | constructor(password, algorithm) {
142 | this.password = password
143 | this.algorithm = algorithm
144 | this.isWebCrypto = WEB_CRYPTO_AVAILABLE
145 |
146 | if (this.isWebCrypto) {
147 | this.keyPromise = deriveKeyWeb(password, algorithm)
148 | } else {
149 | this.key = deriveKeyNode(password, algorithm)
150 | this.keyPromise = Promise.resolve(this.key)
151 | }
152 | }
153 |
154 | /**
155 | * 生成随机 nonce
156 | */
157 | nonce() {
158 | if (this.isWebCrypto) {
159 | return crypto.getRandomValues(new Uint8Array(16))
160 | }
161 |
162 | const { randomBytes } = getNodeCrypto()
163 | return randomBytes(16)
164 | }
165 |
166 | /**
167 | * 加密数据
168 | */
169 | async encrypt(data, nonce) {
170 | if (this.algorithm === 'none') {
171 | return this.isWebCrypto ? ensureUint8Array(data) : ensureBuffer(data)
172 | }
173 |
174 | if (this.isWebCrypto) {
175 | const key = await this.keyPromise
176 | const cryptoKey = await crypto.subtle.importKey(
177 | 'raw',
178 | key,
179 | { name: 'AES-GCM' },
180 | false,
181 | ['encrypt']
182 | )
183 |
184 | const dataBuffer = ensureUint8Array(data)
185 | const encrypted = await crypto.subtle.encrypt(
186 | {
187 | name: 'AES-GCM',
188 | iv: ensureUint8Array(nonce),
189 | tagLength: 128
190 | },
191 | cryptoKey,
192 | dataBuffer
193 | )
194 |
195 | return new Uint8Array(encrypted)
196 | }
197 |
198 | return encryptNode(this.algorithm, this.key, data, nonce)
199 | }
200 |
201 | /**
202 | * 解密数据
203 | */
204 | async decrypt(data, nonce) {
205 | if (this.algorithm === 'none') {
206 | return this.isWebCrypto ? ensureUint8Array(data) : ensureBuffer(data)
207 | }
208 |
209 | if (this.isWebCrypto) {
210 | try {
211 | const key = await this.keyPromise
212 | const cryptoKey = await crypto.subtle.importKey(
213 | 'raw',
214 | key,
215 | { name: 'AES-GCM' },
216 | false,
217 | ['decrypt']
218 | )
219 |
220 | const dataBuffer = ensureUint8Array(data)
221 | const decrypted = await crypto.subtle.decrypt(
222 | {
223 | name: 'AES-GCM',
224 | iv: ensureUint8Array(nonce),
225 | tagLength: 128
226 | },
227 | cryptoKey,
228 | dataBuffer
229 | )
230 |
231 | return new Uint8Array(decrypted)
232 | } catch (error) {
233 | return null
234 | }
235 | }
236 |
237 | return decryptNode(this.algorithm, this.key, data, nonce)
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | dotenv:
12 | specifier: ^16.4.5
13 | version: 16.6.1
14 | hono:
15 | specifier: ^4.6.14
16 | version: 4.10.3
17 | ws:
18 | specifier: ^8.18.0
19 | version: 8.18.3
20 | devDependencies:
21 | wrangler:
22 | specifier: ^4.42.0
23 | version: 4.42.0
24 |
25 | packages:
26 |
27 | '@cloudflare/kv-asset-handler@0.4.0':
28 | resolution: {integrity: sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==}
29 | engines: {node: '>=18.0.0'}
30 |
31 | '@cloudflare/unenv-preset@2.7.6':
32 | resolution: {integrity: sha512-ykG2nd3trk6jbknRCH69xL3RpGLLbKCrbTbWSOvKEq7s4jH06yLrQlRr/q9IU+dK9p1JY1EXqhFK7VG5KqhzmQ==}
33 | peerDependencies:
34 | unenv: 2.0.0-rc.21
35 | workerd: ^1.20250927.0
36 | peerDependenciesMeta:
37 | workerd:
38 | optional: true
39 |
40 | '@cloudflare/workerd-darwin-64@1.20251001.0':
41 | resolution: {integrity: sha512-y1ST/cCscaRewWRnsHZdWbgiLJbki5UMGd0hMo/FLqjlztwPeDgQ5CGm5jMiCDdw/IBCpWxEukftPYR34rWNog==}
42 | engines: {node: '>=16'}
43 | cpu: [x64]
44 | os: [darwin]
45 |
46 | '@cloudflare/workerd-darwin-arm64@1.20251001.0':
47 | resolution: {integrity: sha512-+z4QHHZ/Yix82zLFYS+ZS2UV09IENFPwDCEKUWfnrM9Km2jOOW3Ua4hJNob1EgQUYs8fFZo7k5O/tpwxMsSbbQ==}
48 | engines: {node: '>=16'}
49 | cpu: [arm64]
50 | os: [darwin]
51 |
52 | '@cloudflare/workerd-linux-64@1.20251001.0':
53 | resolution: {integrity: sha512-hGS+O2V9Mm2XjJUaB9ZHMA5asDUaDjKko42e+accbew0PQR7zrAl1afdII6hMqCLV4tk4GAjvhv281pN4g48rg==}
54 | engines: {node: '>=16'}
55 | cpu: [x64]
56 | os: [linux]
57 |
58 | '@cloudflare/workerd-linux-arm64@1.20251001.0':
59 | resolution: {integrity: sha512-QYaMK+pRgt28N7CX1JlJ+ToegJF9LxzqdT7MjWqPgVj9D2WTyIhBVYl3wYjJRcgOlnn+DRt42+li4T64CPEeuA==}
60 | engines: {node: '>=16'}
61 | cpu: [arm64]
62 | os: [linux]
63 |
64 | '@cloudflare/workerd-windows-64@1.20251001.0':
65 | resolution: {integrity: sha512-ospnDR/FlyRvrv9DSHuxDAXmzEBLDUiAHQrQHda1iUH9HqxnNQ8giz9VlPfq7NIRc7bQ1ZdIYPGLJOY4Q366Ng==}
66 | engines: {node: '>=16'}
67 | cpu: [x64]
68 | os: [win32]
69 |
70 | '@cspotcode/source-map-support@0.8.1':
71 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
72 | engines: {node: '>=12'}
73 |
74 | '@emnapi/runtime@1.5.0':
75 | resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
76 |
77 | '@esbuild/aix-ppc64@0.25.4':
78 | resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==}
79 | engines: {node: '>=18'}
80 | cpu: [ppc64]
81 | os: [aix]
82 |
83 | '@esbuild/android-arm64@0.25.4':
84 | resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==}
85 | engines: {node: '>=18'}
86 | cpu: [arm64]
87 | os: [android]
88 |
89 | '@esbuild/android-arm@0.25.4':
90 | resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==}
91 | engines: {node: '>=18'}
92 | cpu: [arm]
93 | os: [android]
94 |
95 | '@esbuild/android-x64@0.25.4':
96 | resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==}
97 | engines: {node: '>=18'}
98 | cpu: [x64]
99 | os: [android]
100 |
101 | '@esbuild/darwin-arm64@0.25.4':
102 | resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==}
103 | engines: {node: '>=18'}
104 | cpu: [arm64]
105 | os: [darwin]
106 |
107 | '@esbuild/darwin-x64@0.25.4':
108 | resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==}
109 | engines: {node: '>=18'}
110 | cpu: [x64]
111 | os: [darwin]
112 |
113 | '@esbuild/freebsd-arm64@0.25.4':
114 | resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==}
115 | engines: {node: '>=18'}
116 | cpu: [arm64]
117 | os: [freebsd]
118 |
119 | '@esbuild/freebsd-x64@0.25.4':
120 | resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==}
121 | engines: {node: '>=18'}
122 | cpu: [x64]
123 | os: [freebsd]
124 |
125 | '@esbuild/linux-arm64@0.25.4':
126 | resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==}
127 | engines: {node: '>=18'}
128 | cpu: [arm64]
129 | os: [linux]
130 |
131 | '@esbuild/linux-arm@0.25.4':
132 | resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==}
133 | engines: {node: '>=18'}
134 | cpu: [arm]
135 | os: [linux]
136 |
137 | '@esbuild/linux-ia32@0.25.4':
138 | resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==}
139 | engines: {node: '>=18'}
140 | cpu: [ia32]
141 | os: [linux]
142 |
143 | '@esbuild/linux-loong64@0.25.4':
144 | resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==}
145 | engines: {node: '>=18'}
146 | cpu: [loong64]
147 | os: [linux]
148 |
149 | '@esbuild/linux-mips64el@0.25.4':
150 | resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==}
151 | engines: {node: '>=18'}
152 | cpu: [mips64el]
153 | os: [linux]
154 |
155 | '@esbuild/linux-ppc64@0.25.4':
156 | resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==}
157 | engines: {node: '>=18'}
158 | cpu: [ppc64]
159 | os: [linux]
160 |
161 | '@esbuild/linux-riscv64@0.25.4':
162 | resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==}
163 | engines: {node: '>=18'}
164 | cpu: [riscv64]
165 | os: [linux]
166 |
167 | '@esbuild/linux-s390x@0.25.4':
168 | resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==}
169 | engines: {node: '>=18'}
170 | cpu: [s390x]
171 | os: [linux]
172 |
173 | '@esbuild/linux-x64@0.25.4':
174 | resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==}
175 | engines: {node: '>=18'}
176 | cpu: [x64]
177 | os: [linux]
178 |
179 | '@esbuild/netbsd-arm64@0.25.4':
180 | resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==}
181 | engines: {node: '>=18'}
182 | cpu: [arm64]
183 | os: [netbsd]
184 |
185 | '@esbuild/netbsd-x64@0.25.4':
186 | resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==}
187 | engines: {node: '>=18'}
188 | cpu: [x64]
189 | os: [netbsd]
190 |
191 | '@esbuild/openbsd-arm64@0.25.4':
192 | resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==}
193 | engines: {node: '>=18'}
194 | cpu: [arm64]
195 | os: [openbsd]
196 |
197 | '@esbuild/openbsd-x64@0.25.4':
198 | resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==}
199 | engines: {node: '>=18'}
200 | cpu: [x64]
201 | os: [openbsd]
202 |
203 | '@esbuild/sunos-x64@0.25.4':
204 | resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==}
205 | engines: {node: '>=18'}
206 | cpu: [x64]
207 | os: [sunos]
208 |
209 | '@esbuild/win32-arm64@0.25.4':
210 | resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==}
211 | engines: {node: '>=18'}
212 | cpu: [arm64]
213 | os: [win32]
214 |
215 | '@esbuild/win32-ia32@0.25.4':
216 | resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==}
217 | engines: {node: '>=18'}
218 | cpu: [ia32]
219 | os: [win32]
220 |
221 | '@esbuild/win32-x64@0.25.4':
222 | resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==}
223 | engines: {node: '>=18'}
224 | cpu: [x64]
225 | os: [win32]
226 |
227 | '@img/sharp-darwin-arm64@0.33.5':
228 | resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
229 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
230 | cpu: [arm64]
231 | os: [darwin]
232 |
233 | '@img/sharp-darwin-x64@0.33.5':
234 | resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
235 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
236 | cpu: [x64]
237 | os: [darwin]
238 |
239 | '@img/sharp-libvips-darwin-arm64@1.0.4':
240 | resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
241 | cpu: [arm64]
242 | os: [darwin]
243 |
244 | '@img/sharp-libvips-darwin-x64@1.0.4':
245 | resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
246 | cpu: [x64]
247 | os: [darwin]
248 |
249 | '@img/sharp-libvips-linux-arm64@1.0.4':
250 | resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
251 | cpu: [arm64]
252 | os: [linux]
253 |
254 | '@img/sharp-libvips-linux-arm@1.0.5':
255 | resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
256 | cpu: [arm]
257 | os: [linux]
258 |
259 | '@img/sharp-libvips-linux-s390x@1.0.4':
260 | resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
261 | cpu: [s390x]
262 | os: [linux]
263 |
264 | '@img/sharp-libvips-linux-x64@1.0.4':
265 | resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
266 | cpu: [x64]
267 | os: [linux]
268 |
269 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
270 | resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
271 | cpu: [arm64]
272 | os: [linux]
273 |
274 | '@img/sharp-libvips-linuxmusl-x64@1.0.4':
275 | resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
276 | cpu: [x64]
277 | os: [linux]
278 |
279 | '@img/sharp-linux-arm64@0.33.5':
280 | resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
281 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
282 | cpu: [arm64]
283 | os: [linux]
284 |
285 | '@img/sharp-linux-arm@0.33.5':
286 | resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
287 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
288 | cpu: [arm]
289 | os: [linux]
290 |
291 | '@img/sharp-linux-s390x@0.33.5':
292 | resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
293 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
294 | cpu: [s390x]
295 | os: [linux]
296 |
297 | '@img/sharp-linux-x64@0.33.5':
298 | resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
299 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
300 | cpu: [x64]
301 | os: [linux]
302 |
303 | '@img/sharp-linuxmusl-arm64@0.33.5':
304 | resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
305 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
306 | cpu: [arm64]
307 | os: [linux]
308 |
309 | '@img/sharp-linuxmusl-x64@0.33.5':
310 | resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
311 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
312 | cpu: [x64]
313 | os: [linux]
314 |
315 | '@img/sharp-wasm32@0.33.5':
316 | resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
317 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
318 | cpu: [wasm32]
319 |
320 | '@img/sharp-win32-ia32@0.33.5':
321 | resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
322 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
323 | cpu: [ia32]
324 | os: [win32]
325 |
326 | '@img/sharp-win32-x64@0.33.5':
327 | resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
328 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
329 | cpu: [x64]
330 | os: [win32]
331 |
332 | '@jridgewell/resolve-uri@3.1.2':
333 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
334 | engines: {node: '>=6.0.0'}
335 |
336 | '@jridgewell/sourcemap-codec@1.5.5':
337 | resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
338 |
339 | '@jridgewell/trace-mapping@0.3.9':
340 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
341 |
342 | '@poppinss/colors@4.1.5':
343 | resolution: {integrity: sha512-FvdDqtcRCtz6hThExcFOgW0cWX+xwSMWcRuQe5ZEb2m7cVQOAVZOIMt+/v9RxGiD9/OY16qJBXK4CVKWAPalBw==}
344 |
345 | '@poppinss/dumper@0.6.4':
346 | resolution: {integrity: sha512-iG0TIdqv8xJ3Lt9O8DrPRxw1MRLjNpoqiSGU03P/wNLP/s0ra0udPJ1J2Tx5M0J3H/cVyEgpbn8xUKRY9j59kQ==}
347 |
348 | '@poppinss/exception@1.2.2':
349 | resolution: {integrity: sha512-m7bpKCD4QMlFCjA/nKTs23fuvoVFoA83brRKmObCUNmi/9tVu8Ve3w4YQAnJu4q3Tjf5fr685HYIC/IA2zHRSg==}
350 |
351 | '@sindresorhus/is@7.1.0':
352 | resolution: {integrity: sha512-7F/yz2IphV39hiS2zB4QYVkivrptHHh0K8qJJd9HhuWSdvf8AN7NpebW3CcDZDBQsUPMoDKWsY2WWgW7bqOcfA==}
353 | engines: {node: '>=18'}
354 |
355 | '@speed-highlight/core@1.2.7':
356 | resolution: {integrity: sha512-0dxmVj4gxg3Jg879kvFS/msl4s9F3T9UXC1InxgOf7t5NvcPD97u/WTA5vL/IxWHMn7qSxBozqrnnE2wvl1m8g==}
357 |
358 | acorn-walk@8.3.2:
359 | resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==}
360 | engines: {node: '>=0.4.0'}
361 |
362 | acorn@8.14.0:
363 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
364 | engines: {node: '>=0.4.0'}
365 | hasBin: true
366 |
367 | blake3-wasm@2.1.5:
368 | resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==}
369 |
370 | color-convert@2.0.1:
371 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
372 | engines: {node: '>=7.0.0'}
373 |
374 | color-name@1.1.4:
375 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
376 |
377 | color-string@1.9.1:
378 | resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
379 |
380 | color@4.2.3:
381 | resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
382 | engines: {node: '>=12.5.0'}
383 |
384 | cookie@1.0.2:
385 | resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
386 | engines: {node: '>=18'}
387 |
388 | defu@6.1.4:
389 | resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==}
390 |
391 | detect-libc@2.1.1:
392 | resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==}
393 | engines: {node: '>=8'}
394 |
395 | dotenv@16.6.1:
396 | resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
397 | engines: {node: '>=12'}
398 |
399 | error-stack-parser-es@1.0.5:
400 | resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==}
401 |
402 | esbuild@0.25.4:
403 | resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==}
404 | engines: {node: '>=18'}
405 | hasBin: true
406 |
407 | exit-hook@2.2.1:
408 | resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==}
409 | engines: {node: '>=6'}
410 |
411 | exsolve@1.0.7:
412 | resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==}
413 |
414 | fsevents@2.3.3:
415 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
416 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
417 | os: [darwin]
418 |
419 | glob-to-regexp@0.4.1:
420 | resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
421 |
422 | hono@4.10.3:
423 | resolution: {integrity: sha512-2LOYWUbnhdxdL8MNbNg9XZig6k+cZXm5IjHn2Aviv7honhBMOHb+jxrKIeJRZJRmn+htUCKhaicxwXuUDlchRA==}
424 | engines: {node: '>=16.9.0'}
425 |
426 | is-arrayish@0.3.4:
427 | resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==}
428 |
429 | kleur@4.1.5:
430 | resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
431 | engines: {node: '>=6'}
432 |
433 | mime@3.0.0:
434 | resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
435 | engines: {node: '>=10.0.0'}
436 | hasBin: true
437 |
438 | miniflare@4.20251001.0:
439 | resolution: {integrity: sha512-OHd31D2LT8JH+85nVXClV0Z18jxirCohzKNAcZs/fgt4mIkUDtidX3VqR3ovAM0jWooNxrFhB9NSs3iDbiJF7Q==}
440 | engines: {node: '>=18.0.0'}
441 | hasBin: true
442 |
443 | ohash@2.0.11:
444 | resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==}
445 |
446 | path-to-regexp@6.3.0:
447 | resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==}
448 |
449 | pathe@2.0.3:
450 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
451 |
452 | semver@7.7.2:
453 | resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
454 | engines: {node: '>=10'}
455 | hasBin: true
456 |
457 | sharp@0.33.5:
458 | resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
459 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
460 |
461 | simple-swizzle@0.2.4:
462 | resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
463 |
464 | stoppable@1.1.0:
465 | resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==}
466 | engines: {node: '>=4', npm: '>=6'}
467 |
468 | supports-color@10.2.2:
469 | resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==}
470 | engines: {node: '>=18'}
471 |
472 | tslib@2.8.1:
473 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
474 |
475 | ufo@1.6.1:
476 | resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==}
477 |
478 | undici@7.14.0:
479 | resolution: {integrity: sha512-Vqs8HTzjpQXZeXdpsfChQTlafcMQaaIwnGwLam1wudSSjlJeQ3bw1j+TLPePgrCnCpUXx7Ba5Pdpf5OBih62NQ==}
480 | engines: {node: '>=20.18.1'}
481 |
482 | unenv@2.0.0-rc.21:
483 | resolution: {integrity: sha512-Wj7/AMtE9MRnAXa6Su3Lk0LNCfqDYgfwVjwRFVum9U7wsto1imuHqk4kTm7Jni+5A0Hn7dttL6O/zjvUvoo+8A==}
484 |
485 | workerd@1.20251001.0:
486 | resolution: {integrity: sha512-oT/K4YWNhmwpVmGeaHNmF7mLRfgjszlVr7lJtpS4jx5khmxmMzWZEEQRrJEpgzeHP6DOq9qWLPNT0bjMK7TchQ==}
487 | engines: {node: '>=16'}
488 | hasBin: true
489 |
490 | wrangler@4.42.0:
491 | resolution: {integrity: sha512-OZXiUSfGD66OVkncDbjZtqrsH6bWPRQMYc6RmMbkzYm/lEvJ8lvARKcqDgEyq8zDAgJAivlMQLyPtKQoVjQ/4g==}
492 | engines: {node: '>=18.0.0'}
493 | hasBin: true
494 | peerDependencies:
495 | '@cloudflare/workers-types': ^4.20251001.0
496 | peerDependenciesMeta:
497 | '@cloudflare/workers-types':
498 | optional: true
499 |
500 | ws@8.18.0:
501 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
502 | engines: {node: '>=10.0.0'}
503 | peerDependencies:
504 | bufferutil: ^4.0.1
505 | utf-8-validate: '>=5.0.2'
506 | peerDependenciesMeta:
507 | bufferutil:
508 | optional: true
509 | utf-8-validate:
510 | optional: true
511 |
512 | ws@8.18.3:
513 | resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
514 | engines: {node: '>=10.0.0'}
515 | peerDependencies:
516 | bufferutil: ^4.0.1
517 | utf-8-validate: '>=5.0.2'
518 | peerDependenciesMeta:
519 | bufferutil:
520 | optional: true
521 | utf-8-validate:
522 | optional: true
523 |
524 | youch-core@0.3.3:
525 | resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==}
526 |
527 | youch@4.1.0-beta.10:
528 | resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==}
529 |
530 | zod@3.22.3:
531 | resolution: {integrity: sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==}
532 |
533 | snapshots:
534 |
535 | '@cloudflare/kv-asset-handler@0.4.0':
536 | dependencies:
537 | mime: 3.0.0
538 |
539 | '@cloudflare/unenv-preset@2.7.6(unenv@2.0.0-rc.21)(workerd@1.20251001.0)':
540 | dependencies:
541 | unenv: 2.0.0-rc.21
542 | optionalDependencies:
543 | workerd: 1.20251001.0
544 |
545 | '@cloudflare/workerd-darwin-64@1.20251001.0':
546 | optional: true
547 |
548 | '@cloudflare/workerd-darwin-arm64@1.20251001.0':
549 | optional: true
550 |
551 | '@cloudflare/workerd-linux-64@1.20251001.0':
552 | optional: true
553 |
554 | '@cloudflare/workerd-linux-arm64@1.20251001.0':
555 | optional: true
556 |
557 | '@cloudflare/workerd-windows-64@1.20251001.0':
558 | optional: true
559 |
560 | '@cspotcode/source-map-support@0.8.1':
561 | dependencies:
562 | '@jridgewell/trace-mapping': 0.3.9
563 |
564 | '@emnapi/runtime@1.5.0':
565 | dependencies:
566 | tslib: 2.8.1
567 | optional: true
568 |
569 | '@esbuild/aix-ppc64@0.25.4':
570 | optional: true
571 |
572 | '@esbuild/android-arm64@0.25.4':
573 | optional: true
574 |
575 | '@esbuild/android-arm@0.25.4':
576 | optional: true
577 |
578 | '@esbuild/android-x64@0.25.4':
579 | optional: true
580 |
581 | '@esbuild/darwin-arm64@0.25.4':
582 | optional: true
583 |
584 | '@esbuild/darwin-x64@0.25.4':
585 | optional: true
586 |
587 | '@esbuild/freebsd-arm64@0.25.4':
588 | optional: true
589 |
590 | '@esbuild/freebsd-x64@0.25.4':
591 | optional: true
592 |
593 | '@esbuild/linux-arm64@0.25.4':
594 | optional: true
595 |
596 | '@esbuild/linux-arm@0.25.4':
597 | optional: true
598 |
599 | '@esbuild/linux-ia32@0.25.4':
600 | optional: true
601 |
602 | '@esbuild/linux-loong64@0.25.4':
603 | optional: true
604 |
605 | '@esbuild/linux-mips64el@0.25.4':
606 | optional: true
607 |
608 | '@esbuild/linux-ppc64@0.25.4':
609 | optional: true
610 |
611 | '@esbuild/linux-riscv64@0.25.4':
612 | optional: true
613 |
614 | '@esbuild/linux-s390x@0.25.4':
615 | optional: true
616 |
617 | '@esbuild/linux-x64@0.25.4':
618 | optional: true
619 |
620 | '@esbuild/netbsd-arm64@0.25.4':
621 | optional: true
622 |
623 | '@esbuild/netbsd-x64@0.25.4':
624 | optional: true
625 |
626 | '@esbuild/openbsd-arm64@0.25.4':
627 | optional: true
628 |
629 | '@esbuild/openbsd-x64@0.25.4':
630 | optional: true
631 |
632 | '@esbuild/sunos-x64@0.25.4':
633 | optional: true
634 |
635 | '@esbuild/win32-arm64@0.25.4':
636 | optional: true
637 |
638 | '@esbuild/win32-ia32@0.25.4':
639 | optional: true
640 |
641 | '@esbuild/win32-x64@0.25.4':
642 | optional: true
643 |
644 | '@img/sharp-darwin-arm64@0.33.5':
645 | optionalDependencies:
646 | '@img/sharp-libvips-darwin-arm64': 1.0.4
647 | optional: true
648 |
649 | '@img/sharp-darwin-x64@0.33.5':
650 | optionalDependencies:
651 | '@img/sharp-libvips-darwin-x64': 1.0.4
652 | optional: true
653 |
654 | '@img/sharp-libvips-darwin-arm64@1.0.4':
655 | optional: true
656 |
657 | '@img/sharp-libvips-darwin-x64@1.0.4':
658 | optional: true
659 |
660 | '@img/sharp-libvips-linux-arm64@1.0.4':
661 | optional: true
662 |
663 | '@img/sharp-libvips-linux-arm@1.0.5':
664 | optional: true
665 |
666 | '@img/sharp-libvips-linux-s390x@1.0.4':
667 | optional: true
668 |
669 | '@img/sharp-libvips-linux-x64@1.0.4':
670 | optional: true
671 |
672 | '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
673 | optional: true
674 |
675 | '@img/sharp-libvips-linuxmusl-x64@1.0.4':
676 | optional: true
677 |
678 | '@img/sharp-linux-arm64@0.33.5':
679 | optionalDependencies:
680 | '@img/sharp-libvips-linux-arm64': 1.0.4
681 | optional: true
682 |
683 | '@img/sharp-linux-arm@0.33.5':
684 | optionalDependencies:
685 | '@img/sharp-libvips-linux-arm': 1.0.5
686 | optional: true
687 |
688 | '@img/sharp-linux-s390x@0.33.5':
689 | optionalDependencies:
690 | '@img/sharp-libvips-linux-s390x': 1.0.4
691 | optional: true
692 |
693 | '@img/sharp-linux-x64@0.33.5':
694 | optionalDependencies:
695 | '@img/sharp-libvips-linux-x64': 1.0.4
696 | optional: true
697 |
698 | '@img/sharp-linuxmusl-arm64@0.33.5':
699 | optionalDependencies:
700 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
701 | optional: true
702 |
703 | '@img/sharp-linuxmusl-x64@0.33.5':
704 | optionalDependencies:
705 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4
706 | optional: true
707 |
708 | '@img/sharp-wasm32@0.33.5':
709 | dependencies:
710 | '@emnapi/runtime': 1.5.0
711 | optional: true
712 |
713 | '@img/sharp-win32-ia32@0.33.5':
714 | optional: true
715 |
716 | '@img/sharp-win32-x64@0.33.5':
717 | optional: true
718 |
719 | '@jridgewell/resolve-uri@3.1.2': {}
720 |
721 | '@jridgewell/sourcemap-codec@1.5.5': {}
722 |
723 | '@jridgewell/trace-mapping@0.3.9':
724 | dependencies:
725 | '@jridgewell/resolve-uri': 3.1.2
726 | '@jridgewell/sourcemap-codec': 1.5.5
727 |
728 | '@poppinss/colors@4.1.5':
729 | dependencies:
730 | kleur: 4.1.5
731 |
732 | '@poppinss/dumper@0.6.4':
733 | dependencies:
734 | '@poppinss/colors': 4.1.5
735 | '@sindresorhus/is': 7.1.0
736 | supports-color: 10.2.2
737 |
738 | '@poppinss/exception@1.2.2': {}
739 |
740 | '@sindresorhus/is@7.1.0': {}
741 |
742 | '@speed-highlight/core@1.2.7': {}
743 |
744 | acorn-walk@8.3.2: {}
745 |
746 | acorn@8.14.0: {}
747 |
748 | blake3-wasm@2.1.5: {}
749 |
750 | color-convert@2.0.1:
751 | dependencies:
752 | color-name: 1.1.4
753 |
754 | color-name@1.1.4: {}
755 |
756 | color-string@1.9.1:
757 | dependencies:
758 | color-name: 1.1.4
759 | simple-swizzle: 0.2.4
760 |
761 | color@4.2.3:
762 | dependencies:
763 | color-convert: 2.0.1
764 | color-string: 1.9.1
765 |
766 | cookie@1.0.2: {}
767 |
768 | defu@6.1.4: {}
769 |
770 | detect-libc@2.1.1: {}
771 |
772 | dotenv@16.6.1: {}
773 |
774 | error-stack-parser-es@1.0.5: {}
775 |
776 | esbuild@0.25.4:
777 | optionalDependencies:
778 | '@esbuild/aix-ppc64': 0.25.4
779 | '@esbuild/android-arm': 0.25.4
780 | '@esbuild/android-arm64': 0.25.4
781 | '@esbuild/android-x64': 0.25.4
782 | '@esbuild/darwin-arm64': 0.25.4
783 | '@esbuild/darwin-x64': 0.25.4
784 | '@esbuild/freebsd-arm64': 0.25.4
785 | '@esbuild/freebsd-x64': 0.25.4
786 | '@esbuild/linux-arm': 0.25.4
787 | '@esbuild/linux-arm64': 0.25.4
788 | '@esbuild/linux-ia32': 0.25.4
789 | '@esbuild/linux-loong64': 0.25.4
790 | '@esbuild/linux-mips64el': 0.25.4
791 | '@esbuild/linux-ppc64': 0.25.4
792 | '@esbuild/linux-riscv64': 0.25.4
793 | '@esbuild/linux-s390x': 0.25.4
794 | '@esbuild/linux-x64': 0.25.4
795 | '@esbuild/netbsd-arm64': 0.25.4
796 | '@esbuild/netbsd-x64': 0.25.4
797 | '@esbuild/openbsd-arm64': 0.25.4
798 | '@esbuild/openbsd-x64': 0.25.4
799 | '@esbuild/sunos-x64': 0.25.4
800 | '@esbuild/win32-arm64': 0.25.4
801 | '@esbuild/win32-ia32': 0.25.4
802 | '@esbuild/win32-x64': 0.25.4
803 |
804 | exit-hook@2.2.1: {}
805 |
806 | exsolve@1.0.7: {}
807 |
808 | fsevents@2.3.3:
809 | optional: true
810 |
811 | glob-to-regexp@0.4.1: {}
812 |
813 | hono@4.10.3: {}
814 |
815 | is-arrayish@0.3.4: {}
816 |
817 | kleur@4.1.5: {}
818 |
819 | mime@3.0.0: {}
820 |
821 | miniflare@4.20251001.0:
822 | dependencies:
823 | '@cspotcode/source-map-support': 0.8.1
824 | acorn: 8.14.0
825 | acorn-walk: 8.3.2
826 | exit-hook: 2.2.1
827 | glob-to-regexp: 0.4.1
828 | sharp: 0.33.5
829 | stoppable: 1.1.0
830 | undici: 7.14.0
831 | workerd: 1.20251001.0
832 | ws: 8.18.0
833 | youch: 4.1.0-beta.10
834 | zod: 3.22.3
835 | transitivePeerDependencies:
836 | - bufferutil
837 | - utf-8-validate
838 |
839 | ohash@2.0.11: {}
840 |
841 | path-to-regexp@6.3.0: {}
842 |
843 | pathe@2.0.3: {}
844 |
845 | semver@7.7.2: {}
846 |
847 | sharp@0.33.5:
848 | dependencies:
849 | color: 4.2.3
850 | detect-libc: 2.1.1
851 | semver: 7.7.2
852 | optionalDependencies:
853 | '@img/sharp-darwin-arm64': 0.33.5
854 | '@img/sharp-darwin-x64': 0.33.5
855 | '@img/sharp-libvips-darwin-arm64': 1.0.4
856 | '@img/sharp-libvips-darwin-x64': 1.0.4
857 | '@img/sharp-libvips-linux-arm': 1.0.5
858 | '@img/sharp-libvips-linux-arm64': 1.0.4
859 | '@img/sharp-libvips-linux-s390x': 1.0.4
860 | '@img/sharp-libvips-linux-x64': 1.0.4
861 | '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
862 | '@img/sharp-libvips-linuxmusl-x64': 1.0.4
863 | '@img/sharp-linux-arm': 0.33.5
864 | '@img/sharp-linux-arm64': 0.33.5
865 | '@img/sharp-linux-s390x': 0.33.5
866 | '@img/sharp-linux-x64': 0.33.5
867 | '@img/sharp-linuxmusl-arm64': 0.33.5
868 | '@img/sharp-linuxmusl-x64': 0.33.5
869 | '@img/sharp-wasm32': 0.33.5
870 | '@img/sharp-win32-ia32': 0.33.5
871 | '@img/sharp-win32-x64': 0.33.5
872 |
873 | simple-swizzle@0.2.4:
874 | dependencies:
875 | is-arrayish: 0.3.4
876 |
877 | stoppable@1.1.0: {}
878 |
879 | supports-color@10.2.2: {}
880 |
881 | tslib@2.8.1:
882 | optional: true
883 |
884 | ufo@1.6.1: {}
885 |
886 | undici@7.14.0: {}
887 |
888 | unenv@2.0.0-rc.21:
889 | dependencies:
890 | defu: 6.1.4
891 | exsolve: 1.0.7
892 | ohash: 2.0.11
893 | pathe: 2.0.3
894 | ufo: 1.6.1
895 |
896 | workerd@1.20251001.0:
897 | optionalDependencies:
898 | '@cloudflare/workerd-darwin-64': 1.20251001.0
899 | '@cloudflare/workerd-darwin-arm64': 1.20251001.0
900 | '@cloudflare/workerd-linux-64': 1.20251001.0
901 | '@cloudflare/workerd-linux-arm64': 1.20251001.0
902 | '@cloudflare/workerd-windows-64': 1.20251001.0
903 |
904 | wrangler@4.42.0:
905 | dependencies:
906 | '@cloudflare/kv-asset-handler': 0.4.0
907 | '@cloudflare/unenv-preset': 2.7.6(unenv@2.0.0-rc.21)(workerd@1.20251001.0)
908 | blake3-wasm: 2.1.5
909 | esbuild: 0.25.4
910 | miniflare: 4.20251001.0
911 | path-to-regexp: 6.3.0
912 | unenv: 2.0.0-rc.21
913 | workerd: 1.20251001.0
914 | optionalDependencies:
915 | fsevents: 2.3.3
916 | transitivePeerDependencies:
917 | - bufferutil
918 | - utf-8-validate
919 |
920 | ws@8.18.0: {}
921 |
922 | ws@8.18.3: {}
923 |
924 | youch-core@0.3.3:
925 | dependencies:
926 | '@poppinss/exception': 1.2.2
927 | error-stack-parser-es: 1.0.5
928 |
929 | youch@4.1.0-beta.10:
930 | dependencies:
931 | '@poppinss/colors': 4.1.5
932 | '@poppinss/dumper': 0.6.4
933 | '@speed-highlight/core': 1.2.7
934 | cookie: 1.0.2
935 | youch-core: 0.3.3
936 |
937 | zod@3.22.3: {}
938 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------