From d6d94030e08fe64e8ccfd2e3afb0a1fd2b2daf42 Mon Sep 17 00:00:00 2001 From: ladit Date: Sat, 13 Apr 2024 02:06:54 +0800 Subject: [PATCH 1/3] feat: introduce sing-box support --- CONTRIBUTING.md | 15 + docs/guide/custom-provider.md | 78 +- docs/guide/custom-template.md | 75 ++ src/generator/artifact.ts | 6 + src/utils/__tests__/singbox.test.ts | 1052 +++++++++++++++++++++++++++ src/utils/index.ts | 1 + src/utils/singbox.ts | 434 +++++++++++ src/utils/ss.ts | 22 + src/validators/common.ts | 14 + src/validators/http.ts | 4 + src/validators/shadowsocks.ts | 8 +- src/validators/socks5.ts | 8 +- src/validators/trojan.ts | 4 +- src/validators/vless.ts | 12 +- src/validators/vmess.ts | 17 + 15 files changed, 1738 insertions(+), 12 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 src/utils/__tests__/singbox.test.ts create mode 100644 src/utils/singbox.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..f196e63a7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# CONTRIBUTING + +## test locally + +- Run `pnpm run build` after modifing this repo +- Run `pnpm link path/to/surgio` in your test repo (eg. `my-rule-store`) +- Run generate command to check the result + +## versioning + +TODO + +## documentation + +TODO diff --git a/docs/guide/custom-provider.md b/docs/guide/custom-provider.md index d25e52d7f..c765a7ded 100644 --- a/docs/guide/custom-provider.md +++ b/docs/guide/custom-provider.md @@ -45,12 +45,12 @@ module.exports = defineCustomProvider(async function () { ## 订阅类型 -目前 Surgio 支持两种 Provider 类型: +目前 Surgio 支持以下几种 Provider 类型: | 类型 | 描述 | 备注 | | :----------------------------------------------: | ------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- | -| `custom` | 自己维护的节点 | 支持 Shadowsocks, Shadowsocksr, Snell, HTTPS, HTTP, Vmess, Socks5, Tuic | -| `clash` | Clash 配置 | 支持 Shadowsocks, Shadowsocksr, Snell, HTTPS, HTTP, Vmess, Vless, Hysteria 2, Socks5, Tuic | +| `custom` | 自己维护的节点 | 支持 Shadowsocks, Shadowsocksr, Snell, HTTPS, HTTP, Vmess, Vless, Hysteria 2, Socks5, Tuic, Trojan, Wireguard | +| `clash` | Clash 配置 | 支持 Shadowsocks, Shadowsocksr, Snell, HTTPS, HTTP, Vmess, Vless, Hysteria 2, Socks5, Tuic, Trojan, Wireguard | | `trojan` | Trojan 订阅 | Shadowrocket 支持的 Trojan 订阅格式 | | `shadowsocks_json_subscribe` | 针对 Windows 客户端的 Shadowsocks 订阅地址 | 通常命名为 _gui-config.json_ | | `shadowsocks_subscribe` | 通用的 Shadowsocks 订阅地址 | | @@ -159,6 +159,7 @@ module.exports = defineCustomProvider({ tfo: false, // TCP Fast Open tls13: false, // TLS 1.3,适用于 v2ray-plugin mux: false, // 目前仅 Clash + Shadowsocks + v2ray-plugin 可用 + multiplex: {}, // 多路复用,可选,见本页面的 `multiplex多路复用` 部分 } ``` @@ -228,6 +229,7 @@ module.exports = defineCustomProvider({ 'x-key': 'x-value', }, }, + multiplex: {}, // 多路复用,可选,见本页面的 `multiplex多路复用` 部分 } ``` @@ -251,6 +253,7 @@ module.exports = defineCustomProvider({ Host: 'www.example.com', }, }, + multiplex: {}, // 多路复用,可选,见本页面的 `multiplex多路复用` 部分 } ``` @@ -271,6 +274,7 @@ module.exports = defineCustomProvider({ grpcOpts: { serviceName: 'example', }, + multiplex: {}, // 多路复用,可选,见本页面的 `multiplex多路复用` 部分 } ``` @@ -295,6 +299,47 @@ module.exports = defineCustomProvider({ } ``` +#### `network: 'quic'` + +```json5 +{ + nodeName: '🇭🇰HK', + type: 'vmess', + hostname: 'hk.example.com', + method: 'auto', + network: 'quic', + alterId: '64', + port: 8080, + tls: true, + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + udpRelay: true, +} +``` + +#### `network: 'httpupgrade'` + +```json5 +{ + nodeName: '🇭🇰HK', + type: 'vmess', + hostname: 'hk.example.com', + method: 'auto', + network: 'httpupgrade', + alterId: '64', + port: 8080, + tls: false, + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + udpRelay: true, + httpUpgradeOpts: { + path: '/', + host: 'www.example.com', + headers: { + 'x-key': ['x-value'], + } + }, +} +``` + ### Vless Vless 节点遵循和 Vmess 类似的配置规则,除了以下几个差异: @@ -347,6 +392,10 @@ Vless 节点遵循和 Vmess 类似的配置规则,除了以下几个差异: username: 'username', password: 'password', tls13: false, // TLS 1.3 + path: '/', // 可选 + headers: { // 可选 + 'x-key': ['x-value'], + }, } ``` @@ -360,6 +409,10 @@ Vless 节点遵循和 Vmess 类似的配置规则,除了以下几个差异: port: 8080, username: 'username', password: 'password', + path: '/', // 可选 + headers: { // 可选 + 'x-key': ['x-value'], + }, } ``` @@ -380,6 +433,7 @@ Vless 节点遵循和 Vmess 类似的配置规则,除了以下几个差异: network: 'ws', // 可不填 wsPath: '/', // 可选 wsHeaders: {}, // 可选 + multiplex: {}, // 多路复用,可选,见本页面的 `multiplex多路复用` 部分 } ``` @@ -1064,3 +1118,21 @@ module.exports = defineClashProvider({ } }) ``` + +## multiplex多路复用 + +```js +{ + multiplex: { + protocol: '', // smux, yamux, h2mux + maxConnections: 1, // 最大连接数量,与 max_streams 冲突 + minStreams: 1, // 在打开新连接之前,连接中的最小多路复用流数量,与 max_streams 冲突 + maxStreams: 1, // 在打开新连接之前,连接中的最大多路复用流数量,与 max_connections 和 min_streams 冲突 + padding: false, // 启用填充 + brutal: { // 可选,TCP Brutal 拥塞控制算法 + upMbps: 0, // 上行 Mbps + downMbps: 0, // 下行 Mbps + }, + } +} +``` diff --git a/docs/guide/custom-template.md b/docs/guide/custom-template.md index 4ef48ca3d..a3a3ded49 100644 --- a/docs/guide/custom-template.md +++ b/docs/guide/custom-template.md @@ -324,6 +324,81 @@ getClashNodeNames(nodeList, netflixFilter, ['测试节点']); getClashNodeNames(nodeList, netflixFilter, [], ['默认节点']); ``` +### getSingboxNodesString + +`getSingboxNodesString(nodeList, filter?)` + +该方法返回一个字符串,格式为逗号分隔的节点信息json object,便于你组织 sing-box 的前后 outbound。例如: + +```json +"outbounds": [ +{ + "type": "selector", + "tag": "proxy", + "outbounds": {{ getSingboxNodeNames(nodeList, null, ['auto']) | json }}, + "interrupt_exist_connections": false +}, +{ + "type": "urltest", + "tag": "auto", + "outbounds": {{ getSingboxNodeNames(nodeList) | json }}, + "url": "{{ proxyTestUrl }}", + "interrupt_exist_connections": false +}, +{{ getSingboxNodesString(nodeList) }}, +{ + "type": "direct", + "tag": "direct", + "tcp_fast_open": true, + "tcp_multi_path": true +}, +{ + "type": "block", + "tag": "block" +}, +{ + "type": "dns", + "tag": "dns" +} +] +``` + +### getSingboxNodes + +`getSingboxNodes(nodeList, filter?)` + +该方法会返回一个包含有节点信息的数组,可用于编写 sing-box 规则。 + +### getSingboxNodeNames + +`getSingboxNodeNames(nodeList, filter?, prependNodeNames?, defaultNodeNames?)` + +该方法会返回一个包含有节点名称的数组,用于编写 sing-box 规则,可参考上方`getSingboxNodesString`的示例。 + +:::tip 提示 +- `filter` 为可选参数 +- `prependNodeNames` 为可选参数。可以通过这个参数在过滤结果前加入自定义节点名 +- `defaultNodeNames` 为可选参数。可以通过这个参数实现在过滤结果为空的情况下,使用默认的自定义节点名 +::: + +若需要过滤 Netflix 节点则传入: + +```js +getSingboxNodeNames(nodeList, netflixFilter); +``` + +需要过滤 Netflix 节点,并且在前面加入节点 `测试节点` + +```js +getSingboxNodeNames(nodeList, netflixFilter, ['测试节点']); +``` + +需要过滤 Netflix 节点,如果没有 Netflix 相关节点,则使用 `默认节点` + +```js +getSingboxNodeNames(nodeList, netflixFilter, [], ['默认节点']); +``` + ### getLoonNodes `getLoonNodes(nodeList, filter?)` diff --git a/src/generator/artifact.ts b/src/generator/artifact.ts index 76adf1aca..0be14712e 100644 --- a/src/generator/artifact.ts +++ b/src/generator/artifact.ts @@ -47,6 +47,9 @@ import { toBase64, toUrlSafeBase64, getNetworkConcurrency, + getSingboxNodeNames, + getSingboxNodes, + getSingboxNodesString, } from '../utils' import { resolveDomain } from '../utils/dns' import { internalFilters, validateFilter } from '../filters' @@ -129,6 +132,9 @@ export class Artifact extends EventEmitter { getNodeNames, getClashNodes, getClashNodeNames, + getSingboxNodesString, + getSingboxNodes, + getSingboxNodeNames, getSurgeNodes, getSurgeNodeNames, getSurgeWireguardNodes, diff --git a/src/utils/__tests__/singbox.test.ts b/src/utils/__tests__/singbox.test.ts new file mode 100644 index 000000000..9649d6d46 --- /dev/null +++ b/src/utils/__tests__/singbox.test.ts @@ -0,0 +1,1052 @@ +import test from 'ava' + +import { NodeTypeEnum, PossibleNodeConfigType } from '../../types' +import * as singbox from '../singbox' + +const nodeList: ReadonlyArray = [ + { + nodeName: 'disabled', + type: NodeTypeEnum.Shadowsocks, + enable: false, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + }, + { + nodeName: 'ss', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + }, + { + nodeName: 'ss.complex', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + obfs: 'http', + obfsHost: 'example.com', + obfsUri: '/obfs', + skipCertVerify: true, + wsHeaders: { + foo: 'bar', + }, + tls13: true, + mux: true, + multiplex: { + protocol: 'smux', + maxConnections: 2, + minStreams: 1, + maxStreams: 0, + padding: true, + brutal: { + upMbps: 100, + downMbps: 100, + }, + }, + tfo: true, + mptcp: true, + shadowTls: { + version: 3, + password: 'password', + sni: 'example.com', + }, + underlyingProxy: 'vmess.ws', + }, + { + nodeName: 'ss.tls', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + obfs: 'tls', + obfsHost: 'example.com', + }, + { + nodeName: 'ss.http', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + obfs: 'http', + obfsHost: 'example.com', + }, + { + nodeName: 'ss.ws', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + obfs: 'ws', + obfsHost: 'example.com', + obfsUri: '/obfs', + wsHeaders: { + foo: 'bar', + }, + mux: false, + }, + { + nodeName: 'ss.wss', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + obfs: 'wss', + obfsHost: 'example.com', + obfsUri: '/obfs', + wsHeaders: { + foo: 'bar', + }, + mux: true, + }, + { + nodeName: 'ss.quic', + type: NodeTypeEnum.Shadowsocks, + hostname: 'example.com', + port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + obfs: 'quic', + obfsHost: 'example.com', + obfsUri: '/obfs', + wsHeaders: { + foo: 'bar', + }, + mux: true, + }, + { + nodeName: 'vmess.tcpNotSupported', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'tcp', + }, + { + nodeName: 'vmess.h2NotSupported', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'h2', + h2Opts: { + path: '/foo', + host: ['example.com'], + }, + }, + { + nodeName: 'vmess.complex', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'ws', + wsOpts: { + path: '/foo', + headers: { + Host: 'example.com', + }, + }, + tls: true, + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + clientFingerprint: 'chrome2', + multiplex: { + protocol: 'smux', + maxConnections: 2, + minStreams: 1, + maxStreams: 0, + padding: true, + brutal: { + upMbps: 100, + downMbps: 100, + }, + }, + tfo: true, + mptcp: true, + shadowTls: { + version: 3, + password: 'password', + sni: 'example.com', + }, + underlyingProxy: 'ss', + }, + { + nodeName: 'vmess.http', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'http', + httpOpts: { + path: ['/foo'], + headers: { + Host: 'example.com', + }, + method: 'POST', + }, + }, + { + nodeName: 'vmess.ws', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'ws', + wsOpts: { + path: '/foo', + headers: { + Host: 'example.com', + }, + }, + }, + { + nodeName: 'vmess.quic', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'quic', + }, + { + nodeName: 'vmess.grpc', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'grpc', + grpcOpts: { + serviceName: 'example', + }, + }, + { + nodeName: 'vmess.httpupgrade', + type: NodeTypeEnum.Vmess, + hostname: 'example.com', + port: 443, + method: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + alterId: 0, + network: 'httpupgrade', + httpUpgradeOpts: { + path: '/foo', + host: 'example.com', + headers: { + Host: 'example.com', + }, + }, + }, + { + nodeName: 'vless.tcpNotSupported', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'tcp', + }, + { + nodeName: 'vless.h2NotSupported', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'h2', + h2Opts: { + path: '/foo', + host: ['example.com'], + }, + }, + { + nodeName: 'vless.complex', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'ws', + wsOpts: { + path: '/foo', + headers: { + Host: 'example.com', + }, + }, + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + multiplex: { + protocol: 'smux', + maxConnections: 2, + minStreams: 1, + maxStreams: 0, + padding: true, + brutal: { + upMbps: 100, + downMbps: 100, + }, + }, + tfo: true, + mptcp: true, + shadowTls: { + version: 3, + password: 'password', + sni: 'example.com', + }, + underlyingProxy: 'ss', + }, + { + nodeName: 'vless.http', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'http', + httpOpts: { + path: ['/foo'], + headers: { + Host: 'example.com', + }, + method: 'POST', + }, + }, + { + nodeName: 'vless.ws', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'ws', + wsOpts: { + path: '/foo', + headers: { + Host: 'example.com', + }, + }, + }, + { + nodeName: 'vless.quic', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'quic', + }, + { + nodeName: 'vless.grpc', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'grpc', + grpcOpts: { + serviceName: 'example', + }, + }, + { + nodeName: 'vless.httpupgrade', + type: NodeTypeEnum.Vless, + hostname: 'example.com', + port: 443, + method: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + realityOpts: { + publicKey: 'publicKey', + shortId: 'shortId', + }, + clientFingerprint: 'chrome2', + network: 'httpupgrade', + httpUpgradeOpts: { + path: '/foo', + host: 'example.com', + headers: { + Host: 'example.com', + }, + }, + }, + { + nodeName: 'http', + type: NodeTypeEnum.HTTP, + hostname: 'example.com', + port: 80, + username: 'username', + password: 'password', + path: '/foo', + headers: { + Host: 'example.com', + }, + }, + { + nodeName: 'https', + type: NodeTypeEnum.HTTPS, + hostname: 'example.com', + port: 443, + username: 'username', + password: 'password', + path: '/foo', + headers: { + Host: 'example.com', + }, + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + clientFingerprint: 'chrome2', + }, + { + nodeName: 'trojan.tcpNotSupported', + type: NodeTypeEnum.Trojan, + hostname: 'example.com', + port: 443, + password: 'password', + network: 'tcp', + }, + { + nodeName: 'trojan.ws', + type: NodeTypeEnum.Trojan, + hostname: 'example.com', + port: 443, + password: 'password', + network: 'ws', + wsPath: '/foo', + wsHeaders: { + Host: 'example.com', + }, + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + clientFingerprint: 'chrome2', + multiplex: { + protocol: 'smux', + maxConnections: 2, + minStreams: 1, + maxStreams: 0, + padding: true, + brutal: { + upMbps: 100, + downMbps: 100, + }, + }, + }, + { + nodeName: 'socks', + type: NodeTypeEnum.Socks5, + hostname: 'example.com', + port: 8080, + username: 'username', + password: 'password', + udpRelay: true, + tls: true, + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + clientFingerprint: 'chrome2', + }, + { + nodeName: 'tuic.versionNotSupported', + type: NodeTypeEnum.Tuic, + hostname: 'example.com', + port: 443, + token: 'token', + }, + { + nodeName: 'tuic', + type: NodeTypeEnum.Tuic, + hostname: 'example.com', + port: 443, + password: 'password', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + version: 5, + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + clientFingerprint: 'chrome2', + }, + { + nodeName: 'hysteria2', + type: NodeTypeEnum.Hysteria2, + hostname: 'example.com', + port: 443, + password: 'password', + downloadBandwidth: 100, + uploadBandwidth: 100, + obfs: 'salamander', + obfsPassword: 'password', + sni: 'example.com', + tls13: true, + skipCertVerify: true, + alpn: ['h2', 'http/1.1'], + clientFingerprint: 'chrome2', + }, + { + nodeName: 'wg', + type: NodeTypeEnum.Wireguard, + selfIp: '10.0.0.1', + selfIpV6: '2001:db8:85a3::8a2e:370:7334', + privateKey: 'privateKey', + mtu: 1420, + peers: [ + { + publicKey: 'publicKey1', + endpoint: 'wg1.example.com:51820', + allowedIps: '0.0.0.0/0, ::/0', + presharedKey: 'presharedKey1', + reservedBits: [1, 2, 3], + }, + { + publicKey: 'publicKey2', + endpoint: 'wg2.example.com:51820', + allowedIps: '0.0.0.0/0, ::/0', + presharedKey: 'presharedKey2', + reservedBits: [2, 2, 3], + }, + ], + }, +] + +// test('generateExpectedNodes', async (t) => { +// t.log(`[${singbox.getSingboxNodesString(nodeList)}]`) +// t.pass() +// }) +const expectedNodes: Record[] = [ + { + type: 'shadowsocks', + tag: 'ss', + server: 'example.com', + server_port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + }, + { + type: 'shadowsocks', + tag: 'ss.complex', + method: 'chacha20-ietf-poly1305', + password: 'password', + plugin: 'obfs-local', + plugin_opts: 'obfs=http;obfs-host=example.com', + tls: { enabled: true, insecure: true, min_version: '1.3' }, + multiplex: { + protocol: 'smux', + max_connections: 2, + min_streams: 1, + max_streams: 0, + padding: true, + enabled: true, + brutal: { up_mbps: 100, down_mbps: 100 }, + }, + tcp_fast_open: true, + tcp_multi_path: true, + detour: 'ss.complex-shadowtls', + }, + { + type: 'shadowtls', + tag: 'ss.complex-shadowtls', + server: 'example.com', + server_port: 443, + version: 3, + password: 'password', + tls: { enabled: true, server_name: 'example.com' }, + }, + { + type: 'shadowsocks', + tag: 'ss.tls', + server: 'example.com', + server_port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + plugin: 'obfs-local', + plugin_opts: 'obfs=tls;obfs-host=example.com', + }, + { + type: 'shadowsocks', + tag: 'ss.http', + server: 'example.com', + server_port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + plugin: 'obfs-local', + plugin_opts: 'obfs=http;obfs-host=example.com', + }, + { + type: 'shadowsocks', + tag: 'ss.ws', + server: 'example.com', + server_port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + plugin: 'v2ray-plugin', + plugin_opts: 'host=example.com;mode=websocket;mux=0;path=/obfs;tls=false', + }, + { + type: 'shadowsocks', + tag: 'ss.wss', + server: 'example.com', + server_port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + plugin: 'v2ray-plugin', + plugin_opts: 'host=example.com;mode=websocket;path=/obfs;tls=true', + }, + { + type: 'shadowsocks', + tag: 'ss.quic', + server: 'example.com', + server_port: 443, + method: 'chacha20-ietf-poly1305', + password: 'password', + plugin: 'v2ray-plugin', + plugin_opts: 'host=example.com;mode=quic;path=/obfs;tls=true', + }, + { + type: 'vmess', + tag: 'vmess.complex', + security: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + transport: { type: 'ws', path: '/foo', headers: { Host: ['example.com'] } }, + tls: { + enabled: true, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + utls: { enabled: true, fingerprint: 'chrome2' }, + }, + multiplex: { + protocol: 'smux', + max_connections: 2, + min_streams: 1, + max_streams: 0, + padding: true, + enabled: true, + brutal: { up_mbps: 100, down_mbps: 100 }, + }, + tcp_fast_open: true, + tcp_multi_path: true, + detour: 'vmess.complex-shadowtls', + }, + { + type: 'shadowtls', + tag: 'vmess.complex-shadowtls', + server: 'example.com', + server_port: 443, + version: 3, + password: 'password', + tls: { enabled: true, server_name: 'example.com' }, + }, + { + type: 'vmess', + tag: 'vmess.http', + server: 'example.com', + server_port: 443, + security: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + transport: { + type: 'http', + path: '/foo', + method: 'POST', + headers: { Host: ['example.com'] }, + }, + }, + { + type: 'vmess', + tag: 'vmess.ws', + server: 'example.com', + server_port: 443, + security: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + transport: { type: 'ws', path: '/foo', headers: { Host: ['example.com'] } }, + }, + { + type: 'vmess', + tag: 'vmess.quic', + server: 'example.com', + server_port: 443, + security: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + transport: { type: 'quic' }, + }, + { + type: 'vmess', + tag: 'vmess.grpc', + server: 'example.com', + server_port: 443, + security: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + transport: { type: 'grpc', service_name: 'example' }, + }, + { + type: 'vmess', + tag: 'vmess.httpupgrade', + server: 'example.com', + server_port: 443, + security: 'auto', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + transport: { + type: 'httpupgrade', + host: 'example.com', + path: '/foo', + headers: { Host: ['example.com'] }, + }, + }, + { + type: 'vless', + tag: 'vless.complex', + security: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + tls: { + enabled: true, + utls: { enabled: true, fingerprint: 'chrome2' }, + reality: { enabled: true, public_key: 'publicKey', short_id: 'shortId' }, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + }, + transport: { type: 'ws', path: '/foo', headers: { Host: ['example.com'] } }, + multiplex: { + protocol: 'smux', + max_connections: 2, + min_streams: 1, + max_streams: 0, + padding: true, + enabled: true, + brutal: { up_mbps: 100, down_mbps: 100 }, + }, + tcp_fast_open: true, + tcp_multi_path: true, + detour: 'vless.complex-shadowtls', + }, + { + type: 'shadowtls', + tag: 'vless.complex-shadowtls', + server: 'example.com', + server_port: 443, + version: 3, + password: 'password', + tls: { enabled: true, server_name: 'example.com' }, + }, + { + type: 'vless', + tag: 'vless.http', + server: 'example.com', + server_port: 443, + security: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + tls: { + enabled: true, + utls: { enabled: true, fingerprint: 'chrome2' }, + reality: { enabled: true, public_key: 'publicKey', short_id: 'shortId' }, + }, + transport: { + type: 'http', + path: '/foo', + method: 'POST', + headers: { Host: ['example.com'] }, + }, + }, + { + type: 'vless', + tag: 'vless.ws', + server: 'example.com', + server_port: 443, + security: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + tls: { + enabled: true, + utls: { enabled: true, fingerprint: 'chrome2' }, + reality: { enabled: true, public_key: 'publicKey', short_id: 'shortId' }, + }, + transport: { type: 'ws', path: '/foo', headers: { Host: ['example.com'] } }, + }, + { + type: 'vless', + tag: 'vless.quic', + server: 'example.com', + server_port: 443, + security: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + tls: { + enabled: true, + utls: { enabled: true, fingerprint: 'chrome2' }, + reality: { enabled: true, public_key: 'publicKey', short_id: 'shortId' }, + }, + transport: { type: 'quic' }, + }, + { + type: 'vless', + tag: 'vless.grpc', + server: 'example.com', + server_port: 443, + security: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + tls: { + enabled: true, + utls: { enabled: true, fingerprint: 'chrome2' }, + reality: { enabled: true, public_key: 'publicKey', short_id: 'shortId' }, + }, + transport: { type: 'grpc', service_name: 'example' }, + }, + { + type: 'vless', + tag: 'vless.httpupgrade', + server: 'example.com', + server_port: 443, + security: 'none', + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + flow: 'xtls-rprx-vision', + tls: { + enabled: true, + utls: { enabled: true, fingerprint: 'chrome2' }, + reality: { enabled: true, public_key: 'publicKey', short_id: 'shortId' }, + }, + transport: { + type: 'httpupgrade', + host: 'example.com', + path: '/foo', + headers: { Host: ['example.com'] }, + }, + }, + { + type: 'http', + tag: 'http', + server: 'example.com', + server_port: 80, + username: 'username', + password: 'password', + path: '/foo', + headers: { Host: ['example.com'] }, + }, + { + type: 'http', + tag: 'https', + server: 'example.com', + server_port: 443, + username: 'username', + password: 'password', + path: '/foo', + headers: { Host: ['example.com'] }, + tls: { + enabled: true, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + utls: { enabled: true, fingerprint: 'chrome2' }, + }, + }, + { + type: 'trojan', + tag: 'trojan.ws', + server: 'example.com', + server_port: 443, + password: 'password', + transport: { type: 'ws', path: '/foo', headers: { Host: ['example.com'] } }, + tls: { + enabled: true, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + utls: { enabled: true, fingerprint: 'chrome2' }, + }, + multiplex: { + protocol: 'smux', + max_connections: 2, + min_streams: 1, + max_streams: 0, + padding: true, + enabled: true, + brutal: { up_mbps: 100, down_mbps: 100 }, + }, + }, + { + type: 'socks', + tag: 'socks', + server: 'example.com', + server_port: 8080, + username: 'username', + password: 'password', + tls: { + enabled: true, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + utls: { enabled: true, fingerprint: 'chrome2' }, + }, + }, + { + type: 'tuic', + tag: 'tuic', + server: 'example.com', + server_port: 443, + uuid: '1386f85e-657b-4d6e-9d56-78badb75e1fd', + password: 'password', + tls: { + enabled: true, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + utls: { enabled: true, fingerprint: 'chrome2' }, + }, + }, + { + type: 'hysteria2', + tag: 'hysteria2', + server: 'example.com', + server_port: 443, + up_mbps: 100, + down_mbps: 100, + obfs: { type: 'salamander', password: 'password' }, + password: 'password', + tls: { + enabled: true, + server_name: 'example.com', + insecure: true, + alpn: ['h2', 'http/1.1'], + min_version: '1.3', + utls: { enabled: true, fingerprint: 'chrome2' }, + }, + }, + { + type: 'wireguard', + tag: 'wg', + local_address: ['10.0.0.1/32', '2001:db8:85a3::8a2e:370:7334/128'], + private_key: 'privateKey', + peers: [ + { + server: 'wg1.example.com', + server_port: 51820, + public_key: 'publicKey1', + pre_shared_key: 'presharedKey1', + allowed_ips: ['0.0.0.0/0', '::/0'], + reserved: [1, 2, 3], + }, + { + server: 'wg2.example.com', + server_port: 51820, + public_key: 'publicKey2', + pre_shared_key: 'presharedKey2', + allowed_ips: ['0.0.0.0/0', '::/0'], + reserved: [2, 2, 3], + }, + ], + mtu: 1420, + }, +] + +const expectedNodeNames = expectedNodes + .map((node) => node.tag as string) + .filter((name) => !['NotSupported', 'disabled'].some((n) => name.includes(n))) + +test('getSingboxNodeNames', async (t) => { + t.deepEqual(singbox.getSingboxNodeNames(nodeList), expectedNodeNames) + t.deepEqual(singbox.getSingboxNodeNames(nodeList, undefined, ['TEST']), [ + 'TEST', + ...expectedNodeNames, + ]) + t.deepEqual( + singbox.getSingboxNodeNames( + nodeList, + (nodeConfig) => nodeConfig.nodeName === 'ss', + ), + ['ss'], + ) + t.deepEqual( + singbox.getSingboxNodeNames( + nodeList, + (nodeConfig) => nodeConfig.nodeName === 'non-exist', + [], + ['foo'], + ), + ['foo'], + ) +}) + +test('getSingboxNodesString', async (t) => { + t.deepEqual( + JSON.parse(`[${singbox.getSingboxNodesString(nodeList)}]`), + expectedNodes, + ) +}) diff --git a/src/utils/index.ts b/src/utils/index.ts index 56b6fe7fd..b6cb7ea91 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -25,6 +25,7 @@ import { applyFilter } from '../filters' export * from './surge' export * from './surfboard' export * from './clash' +export * from './singbox' export * from './quantumult' export * from './loon' export * from './remote-snippet' diff --git a/src/utils/singbox.ts b/src/utils/singbox.ts new file mode 100644 index 000000000..0adb308e5 --- /dev/null +++ b/src/utils/singbox.ts @@ -0,0 +1,434 @@ +import { createLogger } from '@surgio/logger' +import { ERR_INVALID_FILTER } from '../constant' +import { + NodeFilterType, + NodeTypeEnum, + PossibleNodeConfigType, + SortedNodeFilterType, +} from '../types' +import { applyFilter } from '../filters' +import { + checkNotNullish, + getHostnameFromHost, + getPortFromHost, + pickAndFormatKeys, +} from './index' +import { MultiplexValidator, TlsNodeConfigValidator } from '../validators' +import { stringifySip003Options } from './ss' + +const logger = createLogger({ service: 'surgio:utils:singbox' }) + +export const getSingboxNodes = function ( + list: ReadonlyArray, + filter?: NodeFilterType | SortedNodeFilterType, +) { + return applyFilter(list, filter) + .flatMap(nodeListMapper) + .filter((item): item is Record => checkNotNullish(item)) +} + +export const getSingboxNodesString = function ( + list: ReadonlyArray, + filter?: NodeFilterType | SortedNodeFilterType, +) { + return JSON.stringify(getSingboxNodes(list, filter)).slice(1, -1) +} + +export const getSingboxNodeNames = function ( + list: ReadonlyArray, + filter?: NodeFilterType | SortedNodeFilterType, + prependNodeNames?: ReadonlyArray, + defaultNodeNames?: ReadonlyArray, +): ReadonlyArray { + // istanbul ignore next + if (arguments.length === 2 && typeof filter === 'undefined') { + throw new Error(ERR_INVALID_FILTER) + } + + let result: string[] = [] + if (prependNodeNames) { + result = result.concat(prependNodeNames) + } + result = result.concat(getSingboxNodes(list, filter).map((item) => item.tag)) + if (result.length === 0 && defaultNodeNames) { + result = result.concat(defaultNodeNames) + } + return result +} + +const typeMap = { + [NodeTypeEnum.HTTP]: 'http', + [NodeTypeEnum.HTTPS]: 'http', + [NodeTypeEnum.Shadowsocks]: 'shadowsocks', + [NodeTypeEnum.Vmess]: 'vmess', + [NodeTypeEnum.Vless]: 'vless', + [NodeTypeEnum.Trojan]: 'trojan', + [NodeTypeEnum.Socks5]: 'socks', + [NodeTypeEnum.Tuic]: 'tuic', + [NodeTypeEnum.Wireguard]: 'wireguard', + [NodeTypeEnum.Hysteria2]: 'hysteria2', +} + +/** + * @see https://sing-box.sagernet.org/configuration/outbound/ + */ +function nodeListMapper(nodeConfig: PossibleNodeConfigType) { + if (nodeConfig.type in typeMap === false) { + logger.warn( + `不支持为 sing-box 生成 ${nodeConfig.type} 的节点,节点 ${nodeConfig.nodeName} 会被忽略`, + ) + return null + } + const node: Record = { + type: typeMap[nodeConfig.type as keyof typeof typeMap], + tag: nodeConfig.nodeName, + } + if ('hostname' in nodeConfig) { + node.server = nodeConfig.hostname + } + if ('port' in nodeConfig) { + node.server_port = Number(nodeConfig.port) + } + if ('udpRelay' in nodeConfig && nodeConfig.udpRelay === false) { + node.network = 'tcp' + } + + const setTls = (field: string, v: any) => { + if (!node.tls) { + node.tls = { enabled: true } + } + node.tls[field] = v + } + + switch (nodeConfig.type) { + case NodeTypeEnum.Shadowsocks: + node.method = nodeConfig.method + node.password = nodeConfig.password + if (nodeConfig.obfs) { + if (['tls', 'http'].includes(nodeConfig.obfs)) { + node.plugin = 'obfs-local' + node.plugin_opts = stringifySip003Options( + prune({ + obfs: nodeConfig.obfs, + 'obfs-host': nodeConfig.obfsHost, + }), + ) + } + if (['ws', 'wss', 'quic'].includes(nodeConfig.obfs)) { + node.plugin = 'v2ray-plugin' + node.plugin_opts = stringifySip003Options( + prune({ + mode: nodeConfig.obfs === 'quic' ? 'quic' : 'websocket', + tls: ['wss', 'quic'].includes(nodeConfig.obfs), + host: nodeConfig.obfsHost, + path: nodeConfig.obfsUri, + mux: nodeConfig.mux === false ? 0 : null, + }), + ) + } + } + break + + case NodeTypeEnum.Vless: + case NodeTypeEnum.Vmess: { + node.security = nodeConfig.method + node.uuid = nodeConfig.uuid + if (nodeConfig.type === NodeTypeEnum.Vmess) { + if (nodeConfig.alterId) { + node.alter_id = Number(nodeConfig.alterId) + } + } + if (nodeConfig.type === NodeTypeEnum.Vless) { + node.flow = nodeConfig.flow + if (nodeConfig.realityOpts) { + setTls('utls', { + enabled: true, + fingerprint: nodeConfig.clientFingerprint, + }) + setTls('reality', { + enabled: true, + public_key: nodeConfig.realityOpts.publicKey, + short_id: nodeConfig.realityOpts.shortId, + }) + } + } + + switch (nodeConfig.network) { + case 'http': + node.transport = { + type: 'http', + // host: [], + path: nodeConfig.httpOpts?.path[0], + method: nodeConfig.httpOpts?.method, + headers: normalizeHeaders(nodeConfig.httpOpts?.headers), + // idle_timeout: '15s', + // ping_timeout: '15s', + } + break + + case 'ws': + node.transport = { + type: 'ws', + path: nodeConfig.wsOpts?.path, + headers: normalizeHeaders(nodeConfig.wsOpts?.headers), + // max_early_data: 0, + // early_data_header_name: '', + } + break + + case 'quic': + node.transport = { + type: 'quic', + } + break + + case 'grpc': + node.transport = { + type: 'grpc', + service_name: nodeConfig.grpcOpts?.serviceName, + // idle_timeout: '15s', + // ping_timeout: '15s', + // permit_without_stream: false, + } + break + + case 'httpupgrade': + node.transport = { + type: 'httpupgrade', + host: nodeConfig.httpUpgradeOpts?.host, + path: nodeConfig.httpUpgradeOpts?.path, + headers: normalizeHeaders(nodeConfig.httpUpgradeOpts?.headers), + } + break + + default: + logger.warn( + `sing-box 的 ${nodeConfig.type} 节点不支持 network=${nodeConfig.network},节点 ${nodeConfig.nodeName} 会被忽略`, + ) + return null + } + break + } + + case NodeTypeEnum.HTTP: + case NodeTypeEnum.HTTPS: + node.username = nodeConfig.username + node.password = nodeConfig.password + node.path = nodeConfig.path + node.headers = normalizeHeaders(nodeConfig.headers) + if (nodeConfig.type === NodeTypeEnum.HTTPS) { + setTls('enabled', true) + } + break + + case NodeTypeEnum.Trojan: + node.password = nodeConfig.password + if (nodeConfig.network) { + switch (nodeConfig.network) { + case 'ws': + node.transport = { + type: 'ws', + path: nodeConfig.wsPath, + headers: normalizeHeaders(nodeConfig.wsHeaders), + // max_early_data: 0, + // early_data_header_name: '', + } + break + + default: + logger.warn( + `sing-box 的 ${nodeConfig.type} 节点不支持 network=${nodeConfig.network},节点 ${nodeConfig.nodeName} 会被忽略`, + ) + return null + } + } + break + + case NodeTypeEnum.Socks5: + node.username = nodeConfig.username + node.password = nodeConfig.password + break + + case NodeTypeEnum.Tuic: + if ('uuid' in nodeConfig === false) { + logger.warn( + `sing-box 仅支持 tuic v5,节点 ${nodeConfig.nodeName} 会被忽略`, + ) + return null + } + node.uuid = nodeConfig.uuid + node.password = nodeConfig.password + // congestion_control: 'cubic', + // udp_relay_mode: 'native', + // udp_over_stream: false, + // zero_rtt_handshake: false, + // heartbeat: '10s', + break + + case NodeTypeEnum.Hysteria2: + node.up_mbps = nodeConfig.uploadBandwidth + node.down_mbps = nodeConfig.downloadBandwidth + node.obfs = { + type: nodeConfig.obfs, + password: nodeConfig.obfsPassword, + } + node.password = nodeConfig.password + break + + case NodeTypeEnum.Wireguard: + // const sample = { + // system_interface: false, + // gso: false, + // interface_name: 'wg0', + // local_address: ['10.0.0.2/32'], + // private_key: 'YNXtAzepDqRv9H52osJVDQnznT5AM11eCK3ESpwSt04=', + // peers: [ + // { + // server: '127.0.0.1', + // server_port: 1080, + // public_key: 'Z1XXLsKYkYxuiYjJIkRvtIKFepCYHTgON+GwPq7SOV4=', + // pre_shared_key: '31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=', + // allowed_ips: ['0.0.0.0/0'], + // reserved: [0, 0, 0], + // }, + // ], + // peer_public_key: 'Z1XXLsKYkYxuiYjJIkRvtIKFepCYHTgON+GwPq7SOV4=', + // pre_shared_key: '31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=', + // reserved: [0, 0, 0], + // workers: 4, + // mtu: 1408, + // } + node.local_address = [`${nodeConfig.selfIp}/32`] + if (nodeConfig.selfIpV6) { + node.local_address.push(`${nodeConfig.selfIpV6}/128`) + } + node.private_key = nodeConfig.privateKey + node.peers = nodeConfig.peers.map((peer) => ({ + server: getHostnameFromHost(peer.endpoint), + server_port: getPortFromHost(peer.endpoint), + public_key: peer.publicKey, + pre_shared_key: peer.presharedKey, + allowed_ips: peer.allowedIps?.split(',').map((ip) => ip.trim()), + reserved: peer.reservedBits, + })) + node.mtu = nodeConfig.mtu + break + } + + if ('tls' in nodeConfig && nodeConfig.tls) { + setTls('enabled', true) + } + const r = TlsNodeConfigValidator.safeParse(nodeConfig) + if (r.success) { + const tlsConfig = r.data + if (tlsConfig.sni) { + setTls('server_name', tlsConfig.sni) + } + if (tlsConfig.skipCertVerify) { + setTls('insecure', true) + } + if (tlsConfig.alpn) { + setTls('alpn', tlsConfig.alpn) + } + if (tlsConfig.tls13) { + setTls('min_version', '1.3') + } + if (tlsConfig.clientFingerprint) { + setTls('utls', { + enabled: true, + fingerprint: tlsConfig.clientFingerprint, + }) + } + } + if ('multiplex' in nodeConfig) { + const r = MultiplexValidator.safeParse(nodeConfig.multiplex) + if (r.success) { + const multiplexConfig = r.data + node.multiplex = pickAndFormatKeys( + multiplexConfig, + ['protocol', 'maxConnections', 'minStreams', 'maxStreams', 'padding'], + { keyFormat: 'snakeCase' }, + ) + node.multiplex.enabled = true + if (multiplexConfig.brutal) { + node.multiplex.brutal = pickAndFormatKeys( + multiplexConfig.brutal, + ['upMbps', 'downMbps'], + { keyFormat: 'snakeCase' }, + ) + } + } + } + if (nodeConfig.tfo) { + node.tcp_fast_open = true + } + if (nodeConfig.mptcp) { + node.tcp_multi_path = true + } + if (nodeConfig.underlyingProxy) { + node.detour = nodeConfig.underlyingProxy + } + if (!nodeConfig.shadowTls) { + return prune(node) + } + const { server, server_port, ..._node } = node + const tag = `${node.tag}-shadowtls` + _node.detour = tag + return [ + prune(_node), + prune({ + type: 'shadowtls', + tag: tag, + server: server, + server_port: server_port, + version: nodeConfig.shadowTls.version, + password: nodeConfig.shadowTls.password, + tls: { + enabled: true, + server_name: nodeConfig.shadowTls.sni, + }, + }), + ] +} + +function normalizeHeaders(headers: Record | undefined) { + if (!headers) { + return {} + } + return Object.fromEntries(Object.entries(headers).map(([k, v]) => [k, [v]])) +} + +// delete all undefined / null / [] / {} / '' properties +function prune(obj: Record): Record { + const prunedObj: Record = {} + + for (const key in obj) { + const value = obj[key] + + // Check if the property exists and is not null or undefined + if (value !== null && value !== undefined) { + // Check if the property is an array + if (Array.isArray(value)) { + // Check if the array is empty + if (value.length > 0) { + prunedObj[key] = value + } + } else if (typeof value === 'object') { + // Check if the object is empty + if (Object.keys(value).length > 0) { + prunedObj[key] = prune(value) // Recursively prune the object + } + } else if (typeof value === 'string') { + // Check if the string is not empty + if (value.trim().length > 0) { + prunedObj[key] = value + } + } else { + // Add non-empty string, number, or boolean values + prunedObj[key] = value + } + } + } + + return prunedObj +} diff --git a/src/utils/ss.ts b/src/utils/ss.ts index 70c218833..a5d09db77 100644 --- a/src/utils/ss.ts +++ b/src/utils/ss.ts @@ -46,3 +46,25 @@ export const parseSSUri = (str: string): ShadowsocksNodeConfig => { : null), } } + +// Marshal SIP003 plugin options in PossibleNodeConfigType to formatted string. +// An example is 'a=123;host=https://a.com/foo?bar\=baz&q\\q\=1&w\;w\=2;mode=quic;tls=true', +// where semicolons, equal signs and backslashes MUST be escaped with a backslash. +export const stringifySip003Options = (args: { + [key: string]: any +}): string => { + if (!args) { + return '' + } + + const keys = Object.keys(args).sort() + const pairs: string[] = [] + for (const key of keys) { + pairs.push( + `${key.replace(/([;=\\])/g, '\\$1')}=${args[key] + .toString() + .replace(/([;=\\])/g, '\\$1')}`, + ) + } + return pairs.join(';') +} diff --git a/src/validators/common.ts b/src/validators/common.ts index 79d720da7..805e9b638 100644 --- a/src/validators/common.ts +++ b/src/validators/common.ts @@ -97,3 +97,17 @@ export const TlsNodeConfigValidator = SimpleNodeConfigValidator.extend({ serverCertFingerprintSha256: z.ostring(), clientFingerprint: z.ostring(), }) + +export const MultiplexValidator = z.object({ + protocol: z.enum(['smux', 'yamux', 'h2mux']), + maxConnections: z.number().optional(), + minStreams: z.number().optional(), + maxStreams: z.number().optional(), + padding: z.boolean().optional(), + brutal: z + .object({ + upMbps: z.number(), + downMbps: z.number(), + }) + .optional(), +}) diff --git a/src/validators/http.ts b/src/validators/http.ts index 903a833d1..88266079c 100644 --- a/src/validators/http.ts +++ b/src/validators/http.ts @@ -13,10 +13,14 @@ export const HttpNodeConfigValidator = SimpleNodeConfigValidator.extend({ port: PortValidator, username: z.string(), password: z.string(), + path: z.string().optional(), + headers: z.record(z.string()).optional(), }) export const HttpsNodeConfigValidator = TlsNodeConfigValidator.extend({ type: z.literal(NodeTypeEnum.HTTPS), username: z.string(), password: z.string(), + path: z.string().optional(), + headers: z.record(z.string()).optional(), }) diff --git a/src/validators/shadowsocks.ts b/src/validators/shadowsocks.ts index 90c68f66f..b493f89ba 100644 --- a/src/validators/shadowsocks.ts +++ b/src/validators/shadowsocks.ts @@ -1,7 +1,11 @@ import { z } from 'zod' import { NodeTypeEnum } from '../types' -import { PortValidator, SimpleNodeConfigValidator } from './common' +import { + MultiplexValidator, + PortValidator, + SimpleNodeConfigValidator, +} from './common' export const ShadowsocksNodeConfigValidator = SimpleNodeConfigValidator.extend({ type: z.literal(NodeTypeEnum.Shadowsocks), @@ -16,6 +20,7 @@ export const ShadowsocksNodeConfigValidator = SimpleNodeConfigValidator.extend({ z.literal('http'), z.literal('ws'), z.literal('wss'), + z.literal('quic'), ]) .optional(), obfsHost: z.ostring(), @@ -24,4 +29,5 @@ export const ShadowsocksNodeConfigValidator = SimpleNodeConfigValidator.extend({ wsHeaders: z.record(z.string()).optional(), tls13: z.oboolean(), mux: z.oboolean(), + multiplex: MultiplexValidator.optional(), }) diff --git a/src/validators/socks5.ts b/src/validators/socks5.ts index 8c679dccb..613fea6ef 100644 --- a/src/validators/socks5.ts +++ b/src/validators/socks5.ts @@ -1,17 +1,13 @@ import { z } from 'zod' import { NodeTypeEnum } from '../types' -import { PortValidator, SimpleNodeConfigValidator } from './common' +import { TlsNodeConfigValidator } from './common' -export const Socks5NodeConfigValidator = SimpleNodeConfigValidator.extend({ +export const Socks5NodeConfigValidator = TlsNodeConfigValidator.extend({ type: z.literal(NodeTypeEnum.Socks5), - hostname: z.string(), - port: PortValidator, username: z.ostring(), password: z.ostring(), udpRelay: z.oboolean(), tls: z.oboolean(), - skipCertVerify: z.oboolean(), - sni: z.ostring(), clientCert: z.ostring(), }) diff --git a/src/validators/trojan.ts b/src/validators/trojan.ts index fe2de5771..e323e5910 100644 --- a/src/validators/trojan.ts +++ b/src/validators/trojan.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import { NodeTypeEnum } from '../types' -import { TlsNodeConfigValidator } from './common' +import { MultiplexValidator, TlsNodeConfigValidator } from './common' export const TrojanNodeConfigValidator = TlsNodeConfigValidator.extend({ type: z.literal(NodeTypeEnum.Trojan), @@ -10,4 +10,6 @@ export const TrojanNodeConfigValidator = TlsNodeConfigValidator.extend({ network: z.union([z.literal('tcp'), z.literal('ws')]).optional(), wsPath: z.ostring(), wsHeaders: z.record(z.string()).optional(), + + multiplex: MultiplexValidator.optional(), }) diff --git a/src/validators/vless.ts b/src/validators/vless.ts index 0b4487806..157018a98 100644 --- a/src/validators/vless.ts +++ b/src/validators/vless.ts @@ -1,13 +1,19 @@ import { z } from 'zod' import { NodeTypeEnum } from '../types' -import { PortValidator, TlsNodeConfigValidator } from './common' +import { + MultiplexValidator, + PortValidator, + TlsNodeConfigValidator, +} from './common' import { VmessNetworkValidator, VmessH2OptsValidator, VmessGRPCOptsValidator, VmessHttpOptsValidator, VmessWSOptsValidator, + VmessQuicOptsValidator, + VmessHttpUpgradeOptsValidator, } from './vmess' export const VlessRealityOptsValidator = z.object({ @@ -30,5 +36,9 @@ export const VlessNodeConfigValidator = TlsNodeConfigValidator.extend({ h2Opts: VmessH2OptsValidator.optional(), httpOpts: VmessHttpOptsValidator.optional(), grpcOpts: VmessGRPCOptsValidator.optional(), + quicOpts: VmessQuicOptsValidator.optional(), + httpUpgradeOpts: VmessHttpUpgradeOptsValidator.optional(), realityOpts: VlessRealityOptsValidator.optional(), + + multiplex: MultiplexValidator.optional(), }) diff --git a/src/validators/vmess.ts b/src/validators/vmess.ts index 8f676e55a..9ef8a7999 100644 --- a/src/validators/vmess.ts +++ b/src/validators/vmess.ts @@ -5,6 +5,7 @@ import { PortValidator, SimpleNodeConfigValidator, AlterIdValiator, + MultiplexValidator, } from './common' export const VmessNetworkValidator = z.union([ @@ -13,6 +14,8 @@ export const VmessNetworkValidator = z.union([ z.literal('h2'), z.literal('http'), z.literal('grpc'), + z.literal('quic'), + z.literal('httpupgrade'), ]) export const VmessMethodValidator = z.union([ @@ -42,6 +45,16 @@ export const VmessGRPCOptsValidator = z.object({ serviceName: z.string(), }) +export const VmessQuicOptsValidator = z.object({ + // no field now +}) + +export const VmessHttpUpgradeOptsValidator = z.object({ + path: z.string(), + host: z.string().optional(), + headers: z.record(z.string()).optional(), +}) + /** * @see https://stash.wiki/proxy-protocols/proxy-types#vmess * @see https://wiki.metacubex.one/config/proxies/vmess/ @@ -60,6 +73,8 @@ export const VmessNodeConfigValidator = SimpleNodeConfigValidator.extend({ h2Opts: VmessH2OptsValidator.optional(), httpOpts: VmessHttpOptsValidator.optional(), grpcOpts: VmessGRPCOptsValidator.optional(), + quicOpts: VmessQuicOptsValidator.optional(), + httpUpgradeOpts: VmessHttpUpgradeOptsValidator.optional(), tls: z.oboolean(), sni: z.ostring(), @@ -69,6 +84,8 @@ export const VmessNodeConfigValidator = SimpleNodeConfigValidator.extend({ alpn: z.array(z.string()).nonempty().optional(), clientFingerprint: z.ostring(), + multiplex: MultiplexValidator.optional(), + /** * @deprecated */ From d6e798d6d4001e54fe35b84c49c286eb9b30177f Mon Sep 17 00:00:00 2001 From: Roy Li Date: Sat, 27 Apr 2024 16:03:26 +0200 Subject: [PATCH 2/3] docs: update --- CONTRIBUTING.md | 20 +++++++++---- docs/guide/custom-provider.md | 13 +++++---- docs/guide/custom-template.md | 54 ++++++++++++++++++----------------- 3 files changed, 50 insertions(+), 37 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f196e63a7..8c958afa1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,15 +1,23 @@ # CONTRIBUTING -## test locally +## Development -- Run `pnpm run build` after modifing this repo -- Run `pnpm link path/to/surgio` in your test repo (eg. `my-rule-store`) +- Run `pnpm run dev` to start the development toolchain +- Run `pnpm link -g` to link the package globally +- Run `pnpm link -g surgio` in your local surgio config repository to use the local version of surgio - Run generate command to check the result -## versioning +## Testing -TODO +- Run `pnpm run test` to run the tests +- Tests will be automatically checked by GitHub Actions +- A green pipeline is required to merge a PR -## documentation +## Versioning TODO + +## Documentation + +- Run `pnpm run docs:dev` to start the live preview of the documentation +- Add version tag in Markdown if the feature is only available in/after a specific version diff --git a/docs/guide/custom-provider.md b/docs/guide/custom-provider.md index c765a7ded..563f1e80c 100644 --- a/docs/guide/custom-provider.md +++ b/docs/guide/custom-provider.md @@ -334,7 +334,7 @@ module.exports = defineCustomProvider({ path: '/', host: 'www.example.com', headers: { - 'x-key': ['x-value'], + 'x-key': 'x-value', } }, } @@ -394,7 +394,7 @@ Vless 节点遵循和 Vmess 类似的配置规则,除了以下几个差异: tls13: false, // TLS 1.3 path: '/', // 可选 headers: { // 可选 - 'x-key': ['x-value'], + 'x-key': 'x-value', }, } ``` @@ -411,7 +411,7 @@ Vless 节点遵循和 Vmess 类似的配置规则,除了以下几个差异: password: 'password', path: '/', // 可选 headers: { // 可选 - 'x-key': ['x-value'], + 'x-key': 'x-value', }, } ``` @@ -1119,9 +1119,12 @@ module.exports = defineClashProvider({ }) ``` -## multiplex多路复用 +## Multiplex 多路复用 -```js +- sing-box 的多路复用说明:[链接](https://sing-box.sagernet.org/configuration/shared/multiplex/) +- mihomo 的多路复用说明:[链接](https://wiki.metacubex.one/config/proxies/sing-mux/) + +```json5 { multiplex: { protocol: '', // smux, yamux, h2mux diff --git a/docs/guide/custom-template.md b/docs/guide/custom-template.md index a3a3ded49..d1535f782 100644 --- a/docs/guide/custom-template.md +++ b/docs/guide/custom-template.md @@ -330,37 +330,39 @@ getClashNodeNames(nodeList, netflixFilter, [], ['默认节点']); 该方法返回一个字符串,格式为逗号分隔的节点信息json object,便于你组织 sing-box 的前后 outbound。例如: -```json -"outbounds": [ +```json5 { - "type": "selector", - "tag": "proxy", - "outbounds": {{ getSingboxNodeNames(nodeList, null, ['auto']) | json }}, + "outbounds": [ + { + "type": "selector", + "tag": "proxy", + "outbounds": {{ getSingboxNodeNames(nodeList, null, ['auto']) | json }}, "interrupt_exist_connections": false -}, -{ - "type": "urltest", - "tag": "auto", - "outbounds": {{ getSingboxNodeNames(nodeList) | json }}, + }, + { + "type": "urltest", + "tag": "auto", + "outbounds": {{ getSingboxNodeNames(nodeList) | json }}, "url": "{{ proxyTestUrl }}", "interrupt_exist_connections": false -}, -{{ getSingboxNodesString(nodeList) }}, -{ - "type": "direct", - "tag": "direct", - "tcp_fast_open": true, - "tcp_multi_path": true -}, -{ - "type": "block", - "tag": "block" -}, -{ - "type": "dns", - "tag": "dns" + }, + {{ getSingboxNodesString(nodeList) }}, + { + "type": "direct", + "tag": "direct", + "tcp_fast_open": true, + "tcp_multi_path": true + }, + { + "type": "block", + "tag": "block" + }, + { + "type": "dns", + "tag": "dns" + } + ] } -] ``` ### getSingboxNodes From 46d5faa86cdd35a0720471a7c53e5de4e22dce02 Mon Sep 17 00:00:00 2001 From: Roy Li Date: Sat, 27 Apr 2024 22:14:25 +0200 Subject: [PATCH 3/3] feat: change how sing-box config is generated --- docs/.vuepress/config.ts | 20 +- docs/.vuepress/theme/index.ts | 2 +- docs/guide/client/sing-box.md | 115 ++ docs/guide/custom-artifact.md | 21 +- docs/guide/custom-template.md | 179 +- package.json | 17 +- pnpm-lock.yaml | 1650 +++++++++++------ .../__snapshots__/json-template.test.ts.md | 15 + .../__snapshots__/json-template.test.ts.snap | Bin 0 -> 169 bytes src/generator/__tests__/json-template.test.ts | 207 +++ src/generator/artifact.ts | 34 +- src/generator/index.ts | 5 + src/generator/json-template.ts | 90 + src/index.ts | 1 + src/types.ts | 8 +- src/utils/__tests__/singbox.test.ts | 25 +- src/utils/__tests__/ss.test.ts | 16 +- src/utils/singbox.ts | 24 +- src/utils/ss.ts | 4 +- src/validators/artifact.ts | 8 + test/cli.cli-test.ts | 8 + test/cli.cli-test.ts.snap | 201 ++ test/fixture/plain/surgio.conf.js | 28 +- test/fixture/plain/template/singbox.json | 147 ++ tsconfig.json | 14 +- 25 files changed, 2098 insertions(+), 741 deletions(-) create mode 100644 docs/guide/client/sing-box.md create mode 100644 src/generator/__tests__/__snapshots__/json-template.test.ts.md create mode 100644 src/generator/__tests__/__snapshots__/json-template.test.ts.snap create mode 100644 src/generator/__tests__/json-template.test.ts create mode 100644 src/generator/json-template.ts create mode 100644 test/fixture/plain/template/singbox.json diff --git a/docs/.vuepress/config.ts b/docs/.vuepress/config.ts index f5d5a9232..275e9f481 100644 --- a/docs/.vuepress/config.ts +++ b/docs/.vuepress/config.ts @@ -1,9 +1,10 @@ -import { defineUserConfig, HeadConfig, PluginConfig } from 'vuepress' +import { defineUserConfig, type HeadConfig, type PluginConfig, type UserConfig } from 'vuepress' import { path } from '@vuepress/utils' -import docsearchPlugin from '@vuepress/plugin-docsearch' -import registerComponentsPlugin from '@vuepress/plugin-register-components' +import { docsearchPlugin } from '@vuepress/plugin-docsearch' +import { registerComponentsPlugin } from '@vuepress/plugin-register-components' import { sitemapPlugin } from 'vuepress-plugin-sitemap2' import { umamiAnalyticsPlugin } from 'vuepress-plugin-umami-analytics' +import { viteBundler } from '@vuepress/bundler-vite' import customTheme from './theme' @@ -60,14 +61,16 @@ if (process.env.NODE_ENV === 'production') { sitemapPlugin({ hostname: 'https://surgio.js.org', }), - umamiAnalyticsPlugin({ - id: '444a5a25-af75-4c30-b7a4-6aaba520daf6', - src: 'https://sashimi.royli.dev/sashimi.js', - }), + // umamiAnalyticsPlugin({ + // id: '444a5a25-af75-4c30-b7a4-6aaba520daf6', + // src: 'https://sashimi.royli.dev/sashimi.js', + // }) as any, ) } export default defineUserConfig({ + bundler: viteBundler(), + theme: customTheme, locales: { '/': { lang: 'zh-CN', @@ -78,6 +81,5 @@ export default defineUserConfig({ title: meta.title, description: meta.description, head, - theme: customTheme, plugins, -}) +}) as UserConfig diff --git a/docs/.vuepress/theme/index.ts b/docs/.vuepress/theme/index.ts index 74127469f..1b2e04d94 100644 --- a/docs/.vuepress/theme/index.ts +++ b/docs/.vuepress/theme/index.ts @@ -35,7 +35,7 @@ export default { }, { text: '客户端规则维护指南', - children: ['/guide/client/clash', '/guide/client/examples'], + children: ['/guide/client/sing-box', '/guide/client/clash', '/guide/client/examples'], }, '/guide/api', '/guide/cli', diff --git a/docs/guide/client/sing-box.md b/docs/guide/client/sing-box.md new file mode 100644 index 000000000..69d24b2be --- /dev/null +++ b/docs/guide/client/sing-box.md @@ -0,0 +1,115 @@ +--- +sidebarDepth: 1 +--- + +# sing-box + +> + +因为 sing-box 的配置文件为 JSON 格式,所以我们引入了一种新的方式来维护 sing-box 规则,或是其它 JSON 格式的规则。 + +## 准备 + +首先我们找到一份基础的规则文件,它可能是这样的: + +```json +{ + "inbounds": [], + "outbounds": [ + { + "type": "block", + "tag": "block" + }, + { + "type": "dns", + "tag": "dns" + } + ], + "route": {}, + "experimental": { + "cache_file": { + "enabled": true + }, + "clash_api": { + "external_controller": "127.0.0.1:9090" + } + } +} +``` + +我们看到此时 `outbounds` 已经包含了一些内容,我们要做的就是把节点信息填充到 `outbounds` 中。 + +把这个文件保存在 `tempalte` 目录下,命名为 `singbox.json`。 + +## 编写 Artifact + +```js{9-26} +const { extendOutbounds } = require('surgio'); + +module.exports = { + artifacts: [ + { + name: 'singbox.json', + template: 'singbox', + templateType: 'json', + extendTemplate: extendOutbounds( + ({ getSingboxNodes, getSingboxNodeNames, nodeList }) => [ + { + type: 'direct', + tag: 'direct', + tcp_fast_open: false, + tcp_multi_path: true, + }, + { + type: 'selector', + tag: 'proxy', + outbounds: ['auto', ...getSingboxNodeNames(nodeList)], + // outbounds: getSingboxNodeNames(nodeList), // 如果你不需要 auto 节点 + interrupt_exist_connections: false, + }, + ...getSingboxNodes(nodeList), + ], + ), + provider: 'ss', + }, + ] +} +``` + +这个配置的含义是: + +- `template` 为 `singbox`,即我们刚刚创建的模板文件 +- `extendTemplate` 为 `extendOutbounds`,这个函数会把节点信息填充到 `outbounds` 中 + +第 10 行的 `getSingboxNodes`, `getSingboxNodeNames` 属于「模板方法」,具体有哪些可用的模板方法可以看 [这里](/guide/custom-template.md#模板方法)。 + +## `extendOutbounds` 函数 + +`extendOutbounds` 支持两种写法,一种是直接输入一个不可变的变量,另一种是输入一个函数。变量即确定的不会变化的内容,函数则是相对动态的内容。上面的例子中我们使用了函数的写法。 + +### 直接输入变量 + +```js +const { extendOutbounds } = require('surgio'); + +module.exports = { + artifacts: [ + { + name: 'singbox.json', + template: 'singbox', + templateType: 'json', + extendTemplate: extendOutbounds([ + { + type: 'direct', + tag: 'direct', + tcp_fast_open: false, + tcp_multi_path: true, + }, + ]), + provider: 'ss', + }, + ] +} +``` + +你可以在 [这里](/guide/custom-template.md#模板方法) 查看这篇文章中提到的所有模板方法的文档。 diff --git a/docs/guide/custom-artifact.md b/docs/guide/custom-artifact.md index 0757ef10b..4dc9fa6ba 100644 --- a/docs/guide/custom-artifact.md +++ b/docs/guide/custom-artifact.md @@ -7,7 +7,7 @@ sidebarDepth: 2 Surgio 会根据 Artifact 的值来生成配置文件。你可以一次性配置多个 Artifact,一次性生成所有需要的配置文件。 -```js +```json5 { name: 'SurgeV3.conf', template: 'surge_v3', @@ -33,6 +33,23 @@ Surgio 会根据 Artifact 的值来生成配置文件。你可以一次性配置 模板名。会在 `./template` 目录内寻找同名文件(`.tpl` 后缀可省略)。 +### templateType + +- 类型: `string` +- 默认值: `default` +- 有效值: `default`, `json` +- + +模板类型。默认为 `default`,即以传统方式解析模板文件。 + +### extendTemplate + +- 类型: `function` +- 默认值: `undefined` +- + +拓展 JSON 类型的模板,在编写 sing-box 规则时会用到。 + ### provider - 类型: `string` @@ -81,7 +98,7 @@ Surgio 会根据 Artifact 的值来生成配置文件。你可以一次性配置 例如: -```js +```json5 { customParams: { beta: true, diff --git a/docs/guide/custom-template.md b/docs/guide/custom-template.md index d1535f782..b8599dcc5 100644 --- a/docs/guide/custom-template.md +++ b/docs/guide/custom-template.md @@ -324,63 +324,28 @@ getClashNodeNames(nodeList, netflixFilter, ['测试节点']); getClashNodeNames(nodeList, netflixFilter, [], ['默认节点']); ``` -### getSingboxNodesString - -`getSingboxNodesString(nodeList, filter?)` - -该方法返回一个字符串,格式为逗号分隔的节点信息json object,便于你组织 sing-box 的前后 outbound。例如: - -```json5 -{ - "outbounds": [ - { - "type": "selector", - "tag": "proxy", - "outbounds": {{ getSingboxNodeNames(nodeList, null, ['auto']) | json }}, - "interrupt_exist_connections": false - }, - { - "type": "urltest", - "tag": "auto", - "outbounds": {{ getSingboxNodeNames(nodeList) | json }}, - "url": "{{ proxyTestUrl }}", - "interrupt_exist_connections": false - }, - {{ getSingboxNodesString(nodeList) }}, - { - "type": "direct", - "tag": "direct", - "tcp_fast_open": true, - "tcp_multi_path": true - }, - { - "type": "block", - "tag": "block" - }, - { - "type": "dns", - "tag": "dns" - } - ] -} -``` - ### getSingboxNodes +> + `getSingboxNodes(nodeList, filter?)` 该方法会返回一个包含有节点信息的数组,可用于编写 sing-box 规则。 +:::tip 提示 +- `filter` 为可选参数 +::: + ### getSingboxNodeNames -`getSingboxNodeNames(nodeList, filter?, prependNodeNames?, defaultNodeNames?)` +> -该方法会返回一个包含有节点名称的数组,用于编写 sing-box 规则,可参考上方`getSingboxNodesString`的示例。 +`getSingboxNodeNames(nodeList, filter?)` + +该方法会返回一个包含有节点名称的数组,用于编写 sing-box 规则。 :::tip 提示 - `filter` 为可选参数 -- `prependNodeNames` 为可选参数。可以通过这个参数在过滤结果前加入自定义节点名 -- `defaultNodeNames` 为可选参数。可以通过这个参数实现在过滤结果为空的情况下,使用默认的自定义节点名 ::: 若需要过滤 Netflix 节点则传入: @@ -389,18 +354,6 @@ getClashNodeNames(nodeList, netflixFilter, [], ['默认节点']); getSingboxNodeNames(nodeList, netflixFilter); ``` -需要过滤 Netflix 节点,并且在前面加入节点 `测试节点` - -```js -getSingboxNodeNames(nodeList, netflixFilter, ['测试节点']); -``` - -需要过滤 Netflix 节点,如果没有 Netflix 相关节点,则使用 `默认节点` - -```js -getSingboxNodeNames(nodeList, netflixFilter, [], ['默认节点']); -``` - ### getLoonNodes `getLoonNodes(nodeList, filter?)` @@ -541,6 +494,118 @@ PROCESS-NAME,YT Music 和远程片段一样,`.text` 可以获取到原始的字符串内容。 +## JSON 模板方法 + +### extendOutbounds + +> + +`extendOutbounds(function|object)` + +用于拓展 sing-box 规则的 `outbounds` 字段。 + +#### 函数类型 + +```js +extendOutbounds((props) => { + // props 包含本文中的模板方法和变量 + return props.getSingboxNodes(props.nodeList) +}) +``` + +#### 对象类型 + +```js +extendOutbounds([ + { + type: 'direct', + tag: 'direct', + tcp_fast_open: false, + tcp_multi_path: true, + }, + { + type: 'block', + tag: 'block', + }, +]) +``` + +### createExtendFunction + +> + +`createExtendFunction(string)` + +`extendOutbounds` 其实就是用下面的方法生成的。 + +```js +const { createExtendFunction } = require('surgio') + +const extendOutbounds = createExtendFunction('outbounds') +``` + +### combineExtendFunctions + +> + +`combineExtendFunctions(function1, function2, ...)` + +用于合并多个拓展函数。 + +```js +const { combineExtendFunctions, createExtendFunction } = require('surgio') + +const extendDNS = createExtendFunction('dns') +const extendInbounds = createExtendFunction('inbounds') + +const combined = combineExtendFunctions( + extendDNS({ + nameserver: ['1.1.1.1'] + }), + extendInbounds([ + { + port: 7890, + protocol: 'http', + } + ]), +) +``` + +模板: + +```json +{ + "dns": { + "nameserver": [ + "1.0.0.1" + ] + } +} +``` + +结果: + +```json +{ + "dns": { + "nameserver": [ + "1.0.0.1", + "1.1.1.1" + ] + }, + "inbounds": [ + { + "port": 7890, + "protocol": "http" + } + ] +} +``` + +:::tip 提示 +- 拓展数组时新的配置会被追加到原有配置的后面 +::: + ## 片段 (Snippet) ### 如何使用片段? diff --git a/package.json b/package.json index c5c3635e9..8e98f7a33 100644 --- a/package.json +++ b/package.json @@ -146,12 +146,13 @@ "@types/urlsafe-base64": "^1.0.31", "@typescript-eslint/eslint-plugin": "^7", "@typescript-eslint/parser": "^7", - "@vuepress/client": "2.0.0-beta.62", - "@vuepress/plugin-docsearch": "2.0.0-beta.62", - "@vuepress/plugin-google-analytics": "2.0.0-beta.62", - "@vuepress/plugin-register-components": "2.0.0-beta.62", - "@vuepress/theme-default": "2.0.0-beta.62", - "@vuepress/utils": "2.0.0-beta.62", + "@vuepress/bundler-vite": "2.0.0-rc.7", + "@vuepress/client": "2.0.0-rc.9", + "@vuepress/plugin-docsearch": "2.0.0-rc.26", + "@vuepress/plugin-google-analytics": "2.0.0-rc.21", + "@vuepress/plugin-register-components": "2.0.0-rc.21", + "@vuepress/theme-default": "2.0.0-rc.26", + "@vuepress/utils": "2.0.0-rc.9", "ava": "^5.3.1", "benchmark": "^2.1.4", "bumpp": "^9.4.0", @@ -176,8 +177,8 @@ "ts-node": "^10.9.2", "typescript": "^5.3.3", "vue": "^3.4.21", - "vuepress": "2.0.0-beta.62", - "vuepress-plugin-sitemap2": "2.0.0-beta.205", + "vuepress": "2.0.0-rc.9", + "vuepress-plugin-sitemap2": "2.0.0-rc.16", "vuepress-plugin-umami-analytics": "^1.8.1" }, "ava": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec52d5489..25fc47aa6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -223,24 +223,27 @@ devDependencies: '@typescript-eslint/parser': specifier: ^7 version: 7.1.1(eslint@8.57.0)(typescript@5.3.3) + '@vuepress/bundler-vite': + specifier: 2.0.0-rc.7 + version: 2.0.0-rc.7(@types/node@18.19.21)(sass@1.71.1)(typescript@5.3.3) '@vuepress/client': - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62(typescript@5.3.3) + specifier: 2.0.0-rc.9 + version: 2.0.0-rc.9(typescript@5.3.3) '@vuepress/plugin-docsearch': - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62(@algolia/client-search@4.22.1)(typescript@5.3.3) + specifier: 2.0.0-rc.26 + version: 2.0.0-rc.26(@algolia/client-search@4.22.1)(typescript@5.3.3)(vuepress@2.0.0-rc.9) '@vuepress/plugin-google-analytics': - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62(typescript@5.3.3) + specifier: 2.0.0-rc.21 + version: 2.0.0-rc.21(vuepress@2.0.0-rc.9) '@vuepress/plugin-register-components': - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62(typescript@5.3.3) + specifier: 2.0.0-rc.21 + version: 2.0.0-rc.21(vuepress@2.0.0-rc.9) '@vuepress/theme-default': - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62(typescript@5.3.3) + specifier: 2.0.0-rc.26 + version: 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) '@vuepress/utils': - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62 + specifier: 2.0.0-rc.9 + version: 2.0.0-rc.9 ava: specifier: ^5.3.1 version: 5.3.1 @@ -314,11 +317,11 @@ devDependencies: specifier: ^3.4.21 version: 3.4.21(typescript@5.3.3) vuepress: - specifier: 2.0.0-beta.62 - version: 2.0.0-beta.62(@types/node@18.19.21)(@vuepress/client@2.0.0-beta.62)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3)(vue@3.4.21) + specifier: 2.0.0-rc.9 + version: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) vuepress-plugin-sitemap2: - specifier: 2.0.0-beta.205 - version: 2.0.0-beta.205(typescript@5.3.3)(vuepress@2.0.0-beta.62) + specifier: 2.0.0-rc.16 + version: 2.0.0-rc.16(typescript@5.3.3)(vuepress@2.0.0-rc.9) vuepress-plugin-umami-analytics: specifier: ^1.8.1 version: 1.8.1(typescript@5.3.3) @@ -622,6 +625,14 @@ packages: dependencies: '@babel/types': 7.24.0 + /@babel/parser@7.24.5: + resolution: {integrity: sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==} + engines: {node: '>=6.0.0'} + hasBin: true + dependencies: + '@babel/types': 7.24.0 + dev: true + /@babel/runtime@7.24.0: resolution: {integrity: sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==} engines: {node: '>=6.9.0'} @@ -860,14 +871,14 @@ packages: kuler: 2.0.0 dev: false - /@docsearch/css@3.5.2: - resolution: {integrity: sha512-SPiDHaWKQZpwR2siD0KQUwlStvIAnEyK6tAE2h2Wuoq8ue9skzhlyVQ1ddzOxX6khULnAALDiR/isSF3bnuciA==} + /@docsearch/css@3.6.0: + resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} dev: true - /@docsearch/js@3.5.2(@algolia/client-search@4.22.1): - resolution: {integrity: sha512-p1YFTCDflk8ieHgFJYfmyHBki1D61+U9idwrLh+GQQMrBSP3DLGKpy0XUJtPjAOPltcVbqsTjiPFfH7JImjUNg==} + /@docsearch/js@3.6.0(@algolia/client-search@4.22.1): + resolution: {integrity: sha512-QujhqINEElrkIfKwyyyTfbsfMAYCkylInLYMRqHy7PHc8xTBQCow73tlo/Kc7oIwBrCLf0P3YhjlOeV4v8hevQ==} dependencies: - '@docsearch/react': 3.5.2(@algolia/client-search@4.22.1) + '@docsearch/react': 3.6.0(@algolia/client-search@4.22.1) preact: 10.19.6 transitivePeerDependencies: - '@algolia/client-search' @@ -877,8 +888,8 @@ packages: - search-insights dev: true - /@docsearch/react@3.5.2(@algolia/client-search@4.22.1): - resolution: {integrity: sha512-9Ahcrs5z2jq/DcAvYtvlqEBHImbm4YJI8M9y0x6Tqg598P40HTEkX7hsMcIuThI+hTFxRGZ9hll0Wygm2yEjng==} + /@docsearch/react@3.6.0(@algolia/client-search@4.22.1): + resolution: {integrity: sha512-HUFut4ztcVNmqy9gp/wxNbC7pTOHhgVVkHVGCACTuLhUKUhKAF9KYHJtMiLUJxEqiFLQiuri1fWF8zqwM/cu1w==} peerDependencies: '@types/react': '>= 16.8.0 < 19.0.0' react: '>= 16.8.0 < 19.0.0' @@ -896,14 +907,41 @@ packages: dependencies: '@algolia/autocomplete-core': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1) '@algolia/autocomplete-preset-algolia': 1.9.3(@algolia/client-search@4.22.1)(algoliasearch@4.22.1) - '@docsearch/css': 3.5.2 + '@docsearch/css': 3.6.0 algoliasearch: 4.22.1 transitivePeerDependencies: - '@algolia/client-search' dev: true - /@esbuild/android-arm64@0.17.19: - resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + /@esbuild/aix-ppc64@0.19.12: + resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/aix-ppc64@0.20.2: + resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [aix] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.19.12: + resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.20.2: + resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} cpu: [arm64] os: [android] @@ -911,8 +949,17 @@ packages: dev: true optional: true - /@esbuild/android-arm@0.17.19: - resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + /@esbuild/android-arm@0.19.12: + resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.20.2: + resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} cpu: [arm] os: [android] @@ -920,8 +967,17 @@ packages: dev: true optional: true - /@esbuild/android-x64@0.17.19: - resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + /@esbuild/android-x64@0.19.12: + resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.20.2: + resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} cpu: [x64] os: [android] @@ -929,8 +985,17 @@ packages: dev: true optional: true - /@esbuild/darwin-arm64@0.17.19: - resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + /@esbuild/darwin-arm64@0.19.12: + resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.20.2: + resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] @@ -938,8 +1003,17 @@ packages: dev: true optional: true - /@esbuild/darwin-x64@0.17.19: - resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + /@esbuild/darwin-x64@0.19.12: + resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.20.2: + resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} cpu: [x64] os: [darwin] @@ -947,8 +1021,17 @@ packages: dev: true optional: true - /@esbuild/freebsd-arm64@0.17.19: - resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + /@esbuild/freebsd-arm64@0.19.12: + resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.20.2: + resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] @@ -956,8 +1039,17 @@ packages: dev: true optional: true - /@esbuild/freebsd-x64@0.17.19: - resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + /@esbuild/freebsd-x64@0.19.12: + resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.20.2: + resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] @@ -965,8 +1057,17 @@ packages: dev: true optional: true - /@esbuild/linux-arm64@0.17.19: - resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + /@esbuild/linux-arm64@0.19.12: + resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.20.2: + resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} cpu: [arm64] os: [linux] @@ -974,8 +1075,17 @@ packages: dev: true optional: true - /@esbuild/linux-arm@0.17.19: - resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + /@esbuild/linux-arm@0.19.12: + resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.20.2: + resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} cpu: [arm] os: [linux] @@ -983,8 +1093,17 @@ packages: dev: true optional: true - /@esbuild/linux-ia32@0.17.19: - resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + /@esbuild/linux-ia32@0.19.12: + resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.20.2: + resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} cpu: [ia32] os: [linux] @@ -992,8 +1111,17 @@ packages: dev: true optional: true - /@esbuild/linux-loong64@0.17.19: - resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + /@esbuild/linux-loong64@0.19.12: + resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.20.2: + resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} cpu: [loong64] os: [linux] @@ -1001,8 +1129,17 @@ packages: dev: true optional: true - /@esbuild/linux-mips64el@0.17.19: - resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + /@esbuild/linux-mips64el@0.19.12: + resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.20.2: + resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] @@ -1010,8 +1147,17 @@ packages: dev: true optional: true - /@esbuild/linux-ppc64@0.17.19: - resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + /@esbuild/linux-ppc64@0.19.12: + resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.20.2: + resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] @@ -1019,8 +1165,8 @@ packages: dev: true optional: true - /@esbuild/linux-riscv64@0.17.19: - resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + /@esbuild/linux-riscv64@0.19.12: + resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] @@ -1028,8 +1174,26 @@ packages: dev: true optional: true - /@esbuild/linux-s390x@0.17.19: - resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + /@esbuild/linux-riscv64@0.20.2: + resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.19.12: + resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.20.2: + resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} cpu: [s390x] os: [linux] @@ -1037,8 +1201,17 @@ packages: dev: true optional: true - /@esbuild/linux-x64@0.17.19: - resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + /@esbuild/linux-x64@0.19.12: + resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.20.2: + resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} cpu: [x64] os: [linux] @@ -1046,8 +1219,17 @@ packages: dev: true optional: true - /@esbuild/netbsd-x64@0.17.19: - resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + /@esbuild/netbsd-x64@0.19.12: + resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.20.2: + resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] @@ -1055,8 +1237,17 @@ packages: dev: true optional: true - /@esbuild/openbsd-x64@0.17.19: - resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + /@esbuild/openbsd-x64@0.19.12: + resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.20.2: + resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] @@ -1064,8 +1255,17 @@ packages: dev: true optional: true - /@esbuild/sunos-x64@0.17.19: - resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + /@esbuild/sunos-x64@0.19.12: + resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.20.2: + resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} cpu: [x64] os: [sunos] @@ -1073,8 +1273,17 @@ packages: dev: true optional: true - /@esbuild/win32-arm64@0.17.19: - resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + /@esbuild/win32-arm64@0.19.12: + resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.20.2: + resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} cpu: [arm64] os: [win32] @@ -1082,8 +1291,17 @@ packages: dev: true optional: true - /@esbuild/win32-ia32@0.17.19: - resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + /@esbuild/win32-ia32@0.19.12: + resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.20.2: + resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} cpu: [ia32] os: [win32] @@ -1091,8 +1309,17 @@ packages: dev: true optional: true - /@esbuild/win32-x64@0.17.19: - resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + /@esbuild/win32-x64@0.19.12: + resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.20.2: + resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -1232,13 +1459,6 @@ packages: type-detect: 4.0.8 dev: true - /@mdit-vue/plugin-component@0.12.1: - resolution: {integrity: sha512-L3elbvuKUufXwPLHrmJGd/ijd/QKxfcHXy3kRy4O+P7UIV7HSWePpfB0k+wWee+by3MviYYxjVAi392z+DGy3Q==} - dependencies: - '@types/markdown-it': 13.0.7 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/plugin-component@2.0.0: resolution: {integrity: sha512-cTRxlocav/+mfgDcp0P2z/gWuWBez+iNuN4D+b74LpX4AR6UAx2ZvWtCrUZ8VXrO4eCt1/G0YC/Af7mpIb3aoQ==} dependencies: @@ -1246,15 +1466,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/plugin-frontmatter@0.12.1: - resolution: {integrity: sha512-C6ycNjrJ+T4JgbVxwo9cUkfLacOO841Yl8ogqd5PJmAVpc5cM2OLBkqqkZxNRXos3g9xM1VvIQ7gK/047UNADg==} - dependencies: - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 13.0.7 - gray-matter: 4.0.3 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/plugin-frontmatter@2.0.0: resolution: {integrity: sha512-/LrT6E60QI4XV4mqx3J87hqYXlR7ZyMvndmftR2RGz7cRAwa/xL+kyFLlgrMxkBIKitOShKa3LS/9Ov9b0fU+g==} dependencies: @@ -1264,15 +1475,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/plugin-headers@0.12.1: - resolution: {integrity: sha512-DXAw/iWW8f3qUYMDHgQmamL+XGjnaoeRzdvDseLRyr7gXX4xpYO9OIhe/pv9LzSvUoY7UGYmn4kFeI+0qpWJ+g==} - dependencies: - '@mdit-vue/shared': 0.12.1 - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 13.0.7 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/plugin-headers@2.0.0: resolution: {integrity: sha512-ITMMPCnLEYHHgj3XEUL2l75jsNn8guxNqr26YrMSi1f5zcgq4XVy1LIvfwvJ1puqM6Cc5v4BHk3oAyorAi7l1A==} dependencies: @@ -1282,14 +1484,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/plugin-sfc@0.12.1: - resolution: {integrity: sha512-6j332CsSqumy1+StIM3XphdXG1zj9NXuWestDJrKgS3OLy5P0EAioXScUYiZYysw61ZG+2pP37MW7Hg+eHbyIg==} - dependencies: - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 13.0.7 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/plugin-sfc@2.0.0: resolution: {integrity: sha512-OXrMXOyk0iwdIou2jRoIHIbjskwghkO14C9/OjgVHXSSX+iM/WQ4l4yi1aWmNlbQNjtP8IXcVAyJB9K0DFYmLg==} dependencies: @@ -1298,15 +1492,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/plugin-title@0.12.1: - resolution: {integrity: sha512-JOsiDj+CryGbrTDWUnDAwB9kSkN6o9GDo3udR6BPDgBNVb3zAnx9ZNaRpEhDW1LnQhf9/LYicWJ2eTNRKPcJNQ==} - dependencies: - '@mdit-vue/shared': 0.12.1 - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 13.0.7 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/plugin-title@2.0.0: resolution: {integrity: sha512-eqBoETPVkMXNLvwFshz/A2+Cz81VB5HEkXDm0tt6RBW/rTvnoWmGJ1Z+mvcjR5ck5W4nYdIyT68oHxX2JI2M4g==} dependencies: @@ -1316,15 +1501,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/plugin-toc@0.12.1: - resolution: {integrity: sha512-nFGwTwVa8GLCKJMV7cGST7lYuljSjEiCTPgKIpQ/WifwouHsQaL/rnBDr22kpzY2hRTAhM3+TT5GDwLyxa/e6A==} - dependencies: - '@mdit-vue/shared': 0.12.1 - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 13.0.7 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/plugin-toc@2.0.0: resolution: {integrity: sha512-PKQ8sZna3D5chTnt2lxL+ddpyXd++6Nyc0l8VXCeDgStlySQwiP9jaLeeC88oqY4BtRu4cAmILmxDrvuX0Rrdg==} dependencies: @@ -1334,14 +1510,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/shared@0.12.1: - resolution: {integrity: sha512-bXgd0KThe4jC2leCFDFsyrudXIckvTwV4WnQK/rRMrXq0/BAuVdSNdIv1LGCWZxD5+oDyPyEPd0lalTIFwqsmg==} - dependencies: - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 13.0.7 - markdown-it: 13.0.2 - dev: true - /@mdit-vue/shared@2.0.0: resolution: {integrity: sha512-PdxpQpbyTazeo2JT87qms6RPZIzyJd+gwuB+1jSwLDI7+0u5g79y2XgTAbZromSVgY2f3UU5HWdwaLbV9w4uOw==} dependencies: @@ -1350,10 +1518,6 @@ packages: markdown-it: 14.0.0 dev: true - /@mdit-vue/types@0.12.0: - resolution: {integrity: sha512-mrC4y8n88BYvgcgzq9bvTlDgFyi2zuvzmPilRvRc3Uz1iIvq8mDhxJ0rHKFUNzPEScpDvJdIujqiDrulMqiudA==} - dev: true - /@mdit-vue/types@2.0.0: resolution: {integrity: sha512-1BeEB+DbtmDMUAfvbNUj5Hso8cSl2sBVK2iTyOMAqhfDVLdh+/9+D0JmQHaCeUk/vuJoMhOwbweZvh55wHxm4w==} dev: true @@ -1464,6 +1628,134 @@ packages: - typescript dev: true + /@rollup/rollup-android-arm-eabi@4.17.2: + resolution: {integrity: sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.17.2: + resolution: {integrity: sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.17.2: + resolution: {integrity: sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.17.2: + resolution: {integrity: sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.17.2: + resolution: {integrity: sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-musleabihf@4.17.2: + resolution: {integrity: sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.17.2: + resolution: {integrity: sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.17.2: + resolution: {integrity: sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-powerpc64le-gnu@4.17.2: + resolution: {integrity: sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.17.2: + resolution: {integrity: sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-s390x-gnu@4.17.2: + resolution: {integrity: sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.17.2: + resolution: {integrity: sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.17.2: + resolution: {integrity: sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.17.2: + resolution: {integrity: sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.17.2: + resolution: {integrity: sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.17.2: + resolution: {integrity: sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@royli/hygen@6.2.0: resolution: {integrity: sha512-Qs5pC3ETSRQb7iqZ9eAgFxpedyb1Ezu76H/d4HULT/T/YNO3o5QQ/+lwAAfpIFWh5ws3G9TPrwZ3tAKScNIVjQ==} hasBin: true @@ -1639,7 +1931,6 @@ packages: /@types/estree@1.0.5: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} - dev: false /@types/fs-extra@11.0.4: resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} @@ -1724,6 +2015,13 @@ packages: '@types/mdurl': 1.0.5 dev: true + /@types/markdown-it@14.0.1: + resolution: {integrity: sha512-6WfOG3jXR78DW8L5cTYCVVGAsIFZskRHCDo5tbqa+qtKVt4oDRVH7hyIWu1SpDQJlmIoEivNQZ5h+AGAOrgOtQ==} + dependencies: + '@types/linkify-it': 3.0.5 + '@types/mdurl': 1.0.5 + dev: true + /@types/mdurl@1.0.5: resolution: {integrity: sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA==} dev: true @@ -1961,15 +2259,15 @@ packages: /@ungap/structured-clone@1.2.0: resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - /@vitejs/plugin-vue@4.6.2(vite@4.3.9)(vue@3.4.21): - resolution: {integrity: sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw==} - engines: {node: ^14.18.0 || >=16.0.0} + /@vitejs/plugin-vue@5.0.4(vite@5.0.13)(vue@3.4.26): + resolution: {integrity: sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==} + engines: {node: ^18.0.0 || >=20.0.0} peerDependencies: - vite: ^4.0.0 || ^5.0.0 + vite: ^5.0.0 vue: ^3.2.25 dependencies: - vite: 4.3.9(@types/node@18.19.21)(sass@1.71.1) - vue: 3.4.21(typescript@5.3.3) + vite: 5.0.13(@types/node@18.19.21)(sass@1.71.1) + vue: 3.4.26(typescript@5.3.3) dev: true /@vue/compiler-core@3.4.21: @@ -1982,6 +2280,16 @@ packages: source-map-js: 1.0.2 dev: true + /@vue/compiler-core@3.4.26: + resolution: {integrity: sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==} + dependencies: + '@babel/parser': 7.24.5 + '@vue/shared': 3.4.26 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + dev: true + /@vue/compiler-dom@3.4.21: resolution: {integrity: sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==} dependencies: @@ -1989,6 +2297,13 @@ packages: '@vue/shared': 3.4.21 dev: true + /@vue/compiler-dom@3.4.26: + resolution: {integrity: sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==} + dependencies: + '@vue/compiler-core': 3.4.26 + '@vue/shared': 3.4.26 + dev: true + /@vue/compiler-sfc@3.4.21: resolution: {integrity: sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==} dependencies: @@ -2003,6 +2318,20 @@ packages: source-map-js: 1.0.2 dev: true + /@vue/compiler-sfc@3.4.26: + resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==} + dependencies: + '@babel/parser': 7.24.5 + '@vue/compiler-core': 3.4.26 + '@vue/compiler-dom': 3.4.26 + '@vue/compiler-ssr': 3.4.26 + '@vue/shared': 3.4.26 + estree-walker: 2.0.2 + magic-string: 0.30.10 + postcss: 8.4.38 + source-map-js: 1.2.0 + dev: true + /@vue/compiler-ssr@3.4.21: resolution: {integrity: sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==} dependencies: @@ -2010,6 +2339,13 @@ packages: '@vue/shared': 3.4.21 dev: true + /@vue/compiler-ssr@3.4.26: + resolution: {integrity: sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==} + dependencies: + '@vue/compiler-dom': 3.4.26 + '@vue/shared': 3.4.26 + dev: true + /@vue/devtools-api@6.6.1: resolution: {integrity: sha512-LgPscpE3Vs0x96PzSSB4IGVSZXZBZHpfxs+ZA1d+VEPwHdOXowy/Y2CsvCAIFrf+ssVU1pD1jidj505EpUnfbA==} dev: true @@ -2020,6 +2356,12 @@ packages: '@vue/shared': 3.4.21 dev: true + /@vue/reactivity@3.4.26: + resolution: {integrity: sha512-E/ynEAu/pw0yotJeLdvZEsp5Olmxt+9/WqzvKff0gE67tw73gmbx6tRkiagE/eH0UCubzSlGRebCbidB1CpqZQ==} + dependencies: + '@vue/shared': 3.4.26 + dev: true + /@vue/runtime-core@3.4.21: resolution: {integrity: sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==} dependencies: @@ -2027,6 +2369,13 @@ packages: '@vue/shared': 3.4.21 dev: true + /@vue/runtime-core@3.4.26: + resolution: {integrity: sha512-AFJDLpZvhT4ujUgZSIL9pdNcO23qVFh7zWCsNdGQBw8ecLNxOOnPcK9wTTIYCmBJnuPHpukOwo62a2PPivihqw==} + dependencies: + '@vue/reactivity': 3.4.26 + '@vue/shared': 3.4.26 + dev: true + /@vue/runtime-dom@3.4.21: resolution: {integrity: sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==} dependencies: @@ -2035,6 +2384,14 @@ packages: csstype: 3.1.3 dev: true + /@vue/runtime-dom@3.4.26: + resolution: {integrity: sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==} + dependencies: + '@vue/runtime-core': 3.4.26 + '@vue/shared': 3.4.26 + csstype: 3.1.3 + dev: true + /@vue/server-renderer@3.4.21(vue@3.4.21): resolution: {integrity: sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==} peerDependencies: @@ -2045,99 +2402,104 @@ packages: vue: 3.4.21(typescript@5.3.3) dev: true + /@vue/server-renderer@3.4.26(vue@3.4.26): + resolution: {integrity: sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==} + peerDependencies: + vue: 3.4.26 + dependencies: + '@vue/compiler-ssr': 3.4.26 + '@vue/shared': 3.4.26 + vue: 3.4.26(typescript@5.3.3) + dev: true + /@vue/shared@3.4.21: resolution: {integrity: sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==} dev: true - /@vuepress/bundler-vite@2.0.0-beta.62(@types/node@18.19.21)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3): - resolution: {integrity: sha512-Dpb4rJycssM1gs3MlQ5z0cwQ0KCx9Iliojt+qs5lVIUHP9vfw6ANYx51R3ojctt3dCoWfC4bAL4NhGQndGKvrQ==} + /@vue/shared@3.4.26: + resolution: {integrity: sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==} + dev: true + + /@vuepress/bundler-vite@2.0.0-rc.7(@types/node@18.19.21)(sass@1.71.1)(typescript@5.3.3): + resolution: {integrity: sha512-2jNnU3sgHJuUCPfE0DvGRsAxo/A/Locguvnv4Q6QwJYdB1fuAqE6x5p8RDzq8Lv/GsyeexDWiV/PFER5EtVP3w==} dependencies: - '@vitejs/plugin-vue': 4.6.2(vite@4.3.9)(vue@3.4.21) - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - autoprefixer: 10.4.18(postcss@8.4.35) + '@vitejs/plugin-vue': 5.0.4(vite@5.0.13)(vue@3.4.26) + '@vuepress/client': 2.0.0-rc.7(typescript@5.3.3) + '@vuepress/core': 2.0.0-rc.7(typescript@5.3.3) + '@vuepress/shared': 2.0.0-rc.7 + '@vuepress/utils': 2.0.0-rc.7 + autoprefixer: 10.4.19(postcss@8.4.38) connect-history-api-fallback: 2.0.0 - postcss: 8.4.35 - postcss-load-config: 4.0.2(postcss@8.4.35)(ts-node@10.9.2) - rollup: 3.29.4 - vite: 4.3.9(@types/node@18.19.21)(sass@1.71.1) - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) + postcss: 8.4.38 + postcss-load-config: 5.0.3(postcss@8.4.38) + rollup: 4.17.2 + vite: 5.0.13(@types/node@18.19.21)(sass@1.71.1) + vue: 3.4.26(typescript@5.3.3) + vue-router: 4.3.0(vue@3.4.26) transitivePeerDependencies: - '@types/node' + - '@vue/composition-api' + - jiti - less + - lightningcss - sass - stylus - sugarss - supports-color - terser - - ts-node - typescript dev: true - /@vuepress/cli@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-z5mpxORVSZUWsSGtA0bqvsd4vhMDWXAGnQfHjYZ5ylUgnYMxBZMRWrQcpz9doMCk5Qkn56B2s2jKZEvhyFvdAg==} + /@vuepress/cli@2.0.0-rc.9(typescript@5.3.3): + resolution: {integrity: sha512-uv7Xmv3QmPpzCaUAq0oKEwp2tY64AO+7mxamgr7tr+t6FEnCYqr+X0nLlH17UtMkmGWIsbHLIlMjteprxGxIMg==} hasBin: true dependencies: - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 + '@vuepress/core': 2.0.0-rc.9(typescript@5.3.3) + '@vuepress/shared': 2.0.0-rc.9 + '@vuepress/utils': 2.0.0-rc.9 cac: 6.7.14 chokidar: 3.6.0 envinfo: 7.11.1 - esbuild: 0.17.19 + esbuild: 0.20.2 transitivePeerDependencies: - supports-color - typescript dev: true - /@vuepress/client@2.0.0-beta.61(typescript@5.3.3): - resolution: {integrity: sha512-C5QbdQkPsurEsKUkLclVucUAKMzBph9kHMUvfKHJqBaAsiXKYVLa61AICTJeyDkhTYF0faOjmpqmaElfMt1S9w==} + /@vuepress/client@2.0.0-rc.2(typescript@5.3.3): + resolution: {integrity: sha512-gQ4CfBhzWYOCW4OcAUd6S8Jr9m/8UkZZuN/70t12GltbX/cdm6zrGnf89GiVjgvoK8+OYoc7luoBuWbyc/X5sg==} dependencies: '@vue/devtools-api': 6.6.1 - '@vuepress/shared': 2.0.0-beta.61 + '@vuepress/shared': 2.0.0-rc.2 + '@vueuse/core': 10.9.0(vue@3.4.21) vue: 3.4.21(typescript@5.3.3) vue-router: 4.3.0(vue@3.4.21) transitivePeerDependencies: + - '@vue/composition-api' - typescript dev: true - /@vuepress/client@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-5JT0H6EibhZMVmg1fel2BWFFaAEv5zOoD397LOiMQmcEuUneeKNSwGcLrJDyvv8AOXz4wsXwET/to3TsOFoHDQ==} + /@vuepress/client@2.0.0-rc.7(typescript@5.3.3): + resolution: {integrity: sha512-T8jf9h8dfP1ln/7uOIiTAJrdyKRTyq1x+RHXuc7GoRxTfOw+bIYgGvh63Z7m1e3K/yh1nO9aM1WcXbDN1Swp4w==} dependencies: '@vue/devtools-api': 6.6.1 - '@vuepress/shared': 2.0.0-beta.62 - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) + '@vuepress/shared': 2.0.0-rc.7 + '@vueuse/core': 10.9.0(vue@3.4.26) + vue: 3.4.26(typescript@5.3.3) + vue-router: 4.3.0(vue@3.4.26) transitivePeerDependencies: + - '@vue/composition-api' - typescript dev: true - /@vuepress/client@2.0.0-rc.2(typescript@5.3.3): - resolution: {integrity: sha512-gQ4CfBhzWYOCW4OcAUd6S8Jr9m/8UkZZuN/70t12GltbX/cdm6zrGnf89GiVjgvoK8+OYoc7luoBuWbyc/X5sg==} + /@vuepress/client@2.0.0-rc.9(typescript@5.3.3): + resolution: {integrity: sha512-V5jA6L1nHQ8tXBshRHBJKei7HPFonGxFzmVK5yjj2Ho/Xtp/SD9rBS6dyYd5CSkKRGQDgy19Z+BUUPXtdI1qzg==} dependencies: '@vue/devtools-api': 6.6.1 - '@vuepress/shared': 2.0.0-rc.2 - '@vueuse/core': 10.9.0(vue@3.4.21) + '@vuepress/shared': 2.0.0-rc.9 vue: 3.4.21(typescript@5.3.3) vue-router: 4.3.0(vue@3.4.21) transitivePeerDependencies: - - '@vue/composition-api' - - typescript - dev: true - - /@vuepress/core@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-IyL1lxkRg2PO6oFDcioa5YKckKO8jEIwPaNG4mwv7bIEwaN5kpsROVtBeYHKkcnncWQMrbBG/z8aHDvjO/vFJA==} - dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/markdown': 2.0.0-beta.62 - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - vue: 3.4.21(typescript@5.3.3) - transitivePeerDependencies: - - supports-color - typescript dev: true @@ -2155,27 +2517,46 @@ packages: - typescript dev: true - /@vuepress/markdown@2.0.0-beta.62: - resolution: {integrity: sha512-OTGSHDALEE1zgAJgx9Py1AKR1JA/eLTjw63ul77ymt/5eNlU8/EVJg8Pj2nwL3cpvCpzB6sQ1Xkj4TF7D0aD1Q==} + /@vuepress/core@2.0.0-rc.7(typescript@5.3.3): + resolution: {integrity: sha512-SPd2C9MIwHLe4mEWJfbcTHT5caS/vW+oeP79wAW0otSGgn2uY3Mmu5qcYPzl+17o8EYv7vDvPiO+uitp/Cek+A==} dependencies: - '@mdit-vue/plugin-component': 0.12.1 - '@mdit-vue/plugin-frontmatter': 0.12.1 - '@mdit-vue/plugin-headers': 0.12.1 - '@mdit-vue/plugin-sfc': 0.12.1 - '@mdit-vue/plugin-title': 0.12.1 - '@mdit-vue/plugin-toc': 0.12.1 - '@mdit-vue/shared': 0.12.1 - '@mdit-vue/types': 0.12.0 - '@types/markdown-it': 12.2.3 - '@types/markdown-it-emoji': 2.0.4 - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - markdown-it: 13.0.2 - markdown-it-anchor: 8.6.7(@types/markdown-it@12.2.3)(markdown-it@13.0.2) - markdown-it-emoji: 2.0.2 - mdurl: 1.0.1 + '@vuepress/client': 2.0.0-rc.7(typescript@5.3.3) + '@vuepress/markdown': 2.0.0-rc.7 + '@vuepress/shared': 2.0.0-rc.7 + '@vuepress/utils': 2.0.0-rc.7 + vue: 3.4.26(typescript@5.3.3) + transitivePeerDependencies: + - '@vue/composition-api' + - supports-color + - typescript + dev: true + + /@vuepress/core@2.0.0-rc.9(typescript@5.3.3): + resolution: {integrity: sha512-uvMkIqYJ7vjfYEC91rMmT8YJt8xXnob5YYY3TzlwWUSEv4yoV3nlVu0l6Zfhenx/7FwKaxRJ/ePlUGIgUHBcBw==} + dependencies: + '@vuepress/client': 2.0.0-rc.9(typescript@5.3.3) + '@vuepress/markdown': 2.0.0-rc.9 + '@vuepress/shared': 2.0.0-rc.9 + '@vuepress/utils': 2.0.0-rc.9 + vue: 3.4.26(typescript@5.3.3) transitivePeerDependencies: - supports-color + - typescript + dev: true + + /@vuepress/helper@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-/x5Txye+47UmongbiYzsNSuNBiez4mKnnzW1ldX1e6LtAa71zvNH1KD9/MAKlYs34he0NkVrOysJE9/f79tmig==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + '@vue/shared': 3.4.26 + cheerio: 1.0.0-rc.12 + fflate: 0.8.2 + gray-matter: 4.0.3 + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + transitivePeerDependencies: + - typescript dev: true /@vuepress/markdown@2.0.0-rc.2: @@ -2201,62 +2582,117 @@ packages: - supports-color dev: true - /@vuepress/plugin-active-header-links@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-NUoa0JP2npSydJQvM1oOPEtPCKRmtqpkPLxTeBCP6ucR/eHpCbBMrgYt3w6kdmMJykc/AWFd4oZA1QS/MAoEtw==} + /@vuepress/markdown@2.0.0-rc.7: + resolution: {integrity: sha512-mczvo7MZxxXXj5htDXK22r0a7JjTP5sTlcywtVOTJurzCKp8SNSbr3HdmsAnr9S838Hn9+dkvhs57rqgPG3UHA==} dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 - ts-debounce: 4.0.0 - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) + '@mdit-vue/plugin-component': 2.0.0 + '@mdit-vue/plugin-frontmatter': 2.0.0 + '@mdit-vue/plugin-headers': 2.0.0 + '@mdit-vue/plugin-sfc': 2.0.0 + '@mdit-vue/plugin-title': 2.0.0 + '@mdit-vue/plugin-toc': 2.0.0 + '@mdit-vue/shared': 2.0.0 + '@mdit-vue/types': 2.0.0 + '@types/markdown-it': 13.0.7 + '@types/markdown-it-emoji': 2.0.4 + '@vuepress/shared': 2.0.0-rc.7 + '@vuepress/utils': 2.0.0-rc.7 + markdown-it: 14.1.0 + markdown-it-anchor: 8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.1.0) + markdown-it-emoji: 3.0.0 + mdurl: 2.0.0 transitivePeerDependencies: - supports-color - - typescript dev: true - /@vuepress/plugin-back-to-top@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-ndStdKobpq7/YxhtUg2YrSkd8FNoE0v4pPVdTBND6jlkPns4CCcyu+w6BZ8mkiiB2dzS27JrhKcXHz1Tsb0nUA==} + /@vuepress/markdown@2.0.0-rc.9: + resolution: {integrity: sha512-e7as2ar3RQp0bUyMiwBPi7L/G2fzscb3s0BywNcAwubFR22o0/dBEYRYdrN0clPQ2FXpPxF6AFj4aD7O1heCbw==} dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 - ts-debounce: 4.0.0 - vue: 3.4.21(typescript@5.3.3) + '@mdit-vue/plugin-component': 2.0.0 + '@mdit-vue/plugin-frontmatter': 2.0.0 + '@mdit-vue/plugin-headers': 2.0.0 + '@mdit-vue/plugin-sfc': 2.0.0 + '@mdit-vue/plugin-title': 2.0.0 + '@mdit-vue/plugin-toc': 2.0.0 + '@mdit-vue/shared': 2.0.0 + '@mdit-vue/types': 2.0.0 + '@types/markdown-it': 13.0.7 + '@types/markdown-it-emoji': 2.0.4 + '@vuepress/shared': 2.0.0-rc.9 + '@vuepress/utils': 2.0.0-rc.9 + markdown-it: 14.1.0 + markdown-it-anchor: 8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.1.0) + markdown-it-emoji: 3.0.0 + mdurl: 2.0.0 transitivePeerDependencies: - supports-color + dev: true + + /@vuepress/plugin-active-header-links@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-8znvmaw0QBX0SXhV49ob3OIrDustpGN8SrJJecVE6d39OThyJ470XAX3vWteyMnsdDnlD0RckqMbhxbTmXbXxw==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + '@vueuse/core': 10.9.0(vue@3.4.26) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + transitivePeerDependencies: + - '@vue/composition-api' - typescript dev: true - /@vuepress/plugin-container@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-ibo0J8ye5KA6zkwIttkVqleSLy4Sq0rcSW+X8cTzyFfoKKs0Y+ECjmf4wRrDl79m+lgpA43mlFpCcbgtmV9aqw==} + /@vuepress/plugin-back-to-top@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-1pkSPmXc6CyhDu/KrXbhF0tUzRbOFEXItCMedvjgYBH1eJ9upUZm/M/xbQICfm/Vt8zB3asU7pIW0Q4RBJk/eQ==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@types/markdown-it': 12.2.3 - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/markdown': 2.0.0-beta.62 - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - markdown-it: 13.0.2 - markdown-it-container: 3.0.0 + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vueuse/core': 10.9.0(vue@3.4.26) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color + - '@vue/composition-api' - typescript dev: true - /@vuepress/plugin-docsearch@2.0.0-beta.62(@algolia/client-search@4.22.1)(typescript@5.3.3): - resolution: {integrity: sha512-XdY4o15CKLqTEn485/pjWLdfV3fsm9JkoDJ97b9Cv0I9T/oW8NV3BFVezdX1d06vxB/doqoGj+JFEJH7t1lEuw==} + /@vuepress/plugin-container@2.0.0-rc.25(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-2yUuBWy09d1JGs6v4siebxN1Uo80XXUv800XfUqZ+Nnlu/362i5Ro2GDYww9MSP3yKKZjTnvIlUeX8S0H0wqrg==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@docsearch/css': 3.5.2 - '@docsearch/js': 3.5.2(@algolia/client-search@4.22.1) - '@docsearch/react': 3.5.2(@algolia/client-search@4.22.1) - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - '@vueuse/core': 10.9.0(vue@3.4.21) + '@types/markdown-it': 14.0.1 + markdown-it: 14.1.0 + markdown-it-container: 4.0.0 + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + dev: true + + /@vuepress/plugin-copy-code@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-kYb0t49cvhJHYam03OSt5bq7mKUaWKU6ko5jex/C6kjEsuSusCWdY7pnJ4PXl63/umFcdJPfTvtaZfbJE5SAHA==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vueuse/core': 10.9.0(vue@3.4.26) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + transitivePeerDependencies: + - '@vue/composition-api' + - typescript + dev: true + + /@vuepress/plugin-docsearch@2.0.0-rc.26(@algolia/client-search@4.22.1)(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-/Wa0FAURLFtxyvoH1TS793GeVvsOWy9VrIeil5wAMwR1O9pI1EOID+BzgeiaWAav4H4/sdwaG1OsB68N9q3MLQ==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + '@docsearch/css': 3.6.0 + '@docsearch/js': 3.6.0(@algolia/client-search@4.22.1) + '@docsearch/react': 3.6.0(@algolia/client-search@4.22.1) + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vueuse/core': 10.9.0(vue@3.4.26) ts-debounce: 4.0.0 - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - '@algolia/client-search' - '@types/react' @@ -2264,213 +2700,228 @@ packages: - react - react-dom - search-insights - - supports-color - typescript dev: true - /@vuepress/plugin-external-link-icon@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-mQ7gj6pMHYCp7zk6N92omxUz9CjKYZtvZXkNmsloZsz0hiGS1SdG29vLo8yKm/qVzyu9F45WgVNcdQD5mkzx3Q==} + /@vuepress/plugin-external-link-icon@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-jTa3JNgV6ZfKRjBm+tjwTh7ZU23cEPcC5alGeYMTyUf9wHkv0fmWJ7IO6HFrilTgXE+ejaj7ouZDqwTYeNej3g==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/markdown': 2.0.0-beta.62 - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - vue: 3.4.21(typescript@5.3.3) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/plugin-git@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-vTYUguI+X5G7JPTySDnZ6lcAGXBWlD1Nsw9IV42Hh4fvevWzZ3WIjkAhjZpdURIz+xQPEZBbgqnOKjBpbPx0jA==} + /@vuepress/plugin-git@2.0.0-rc.22(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-+T50AdCZ68Pkld4r8IEHTXLugfNVCxxPp2G1hlI/lpQ6IZcpLbswMI6l9xbbo15RrOBg/V0jkim/B/jaaVIM6A==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 - execa: 7.2.0 - transitivePeerDependencies: - - supports-color - - typescript + execa: 8.0.1 + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + dev: true + + /@vuepress/plugin-google-analytics@2.0.0-rc.21(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-ZqDw3MrxA3tY5WXD/LIaZoCSgnynO9aQboOUgm1SF8GoR/7ULqiCWmxbLD3L8kkWS3TWKnH+JwK0VG0J6FNyFA==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) dev: true - /@vuepress/plugin-google-analytics@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-Nm2GGb2KCooZjKD0ZgmmlI+2N05/mCMIJ8ziZU/3xrao6oRCxlKFm+g1L0iE/1XY2vNQpX76OedhSwRtYEjsRw==} + /@vuepress/plugin-links-check@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-4npcEmyEOxUx0/+YrvWJ8+Wcy4QZHyLG8vARTB7nz+70VoPIgQT3pysc1l3V282wou0WSsNhRKTN6YqF8k6RYg==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/plugin-medium-zoom@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-1BolO1OE9Dxf4xLpEDEYjWTmx+luD6RSwjM+Wbgp7gBMK98yY8N9rHxWCzhLWbTffVezmAO0ze37l7hVd4ypTA==} + /@vuepress/plugin-medium-zoom@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-JIrMrvHEy+5Jw/xF4hfD90tmYPpMX/v/ltLmQk2tKOOn0DyuFhu9j7urs3roxdJwULf3kWNNIwJEK4xg6sl2Qw==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) medium-zoom: 1.1.0 - vue: 3.4.21(typescript@5.3.3) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/plugin-nprogress@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-w1Qqw1pP7+fXN+Aznmbfdp62XnQJ2s/FJyoGfV7LjVfV+gWFtqymiJiahvd2aQpBi4/qZNvtFJ1SOQf5tn1CxA==} + /@vuepress/plugin-nprogress@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-DXuPEc7TXfNm9yg3cOk9gQzmYBinb0BamJpZEpn3JPibfEsB8M2mzWxDHt/QU/ERSEkBy4BODOUKYq824+LAZQ==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/plugin-palette@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-Tw+KFxC8c3KIGeXANqMXFLoQ96ZQ/hJaKC0qm6iN04Wk9hKYazkxhPZTAZkOG3SrxaxvOrgnzvicpci6FJgnGA==} + /@vuepress/plugin-palette@2.0.0-rc.21(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-jnWzTiM3xHXweD3AKZVTCnuliH/aoIGaV1C5yhIeinXPZHn49syH8wMQ3kAgxWO+Y4xfihiY8E32V33XQ8Lf6w==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 chokidar: 3.6.0 - transitivePeerDependencies: - - supports-color - - typescript + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) dev: true - /@vuepress/plugin-prismjs@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-kPrlh+I4w+YyU6joahjvuMo2zMwbpB36drZYfjXtYFFIxpBQ5Xdse4xx89vYOX0KqckOQrNa/tnYnfBuHBkgAQ==} + /@vuepress/plugin-prismjs@2.0.0-rc.21(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-dMTCu/TZ1QCmTHXL4THVeh9gWzuqkJV8qhck5U77OP1qmgyf+r529A+MTOgp3ddcph1Yzb/FRb2orlefHk+yNQ==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) prismjs: 1.29.0 + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + dev: true + + /@vuepress/plugin-register-components@2.0.0-rc.21(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-kfzM1fYTXJh0z0kmzA/Fwm7iTdcWJMtoncy42p/p/RwGjYtVfQw5PGbV/0mnwwupxsjA+VcOTcrJOcaphsUMUA==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + chokidar: 3.6.0 + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + dev: true + + /@vuepress/plugin-seo@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-V8GVPz6PlKXIHNjPlGpTzokO4mmxIaWkCDHQO/zexP9bIjad+oi8m/UL32hwisw42aEyLBAr13l3pRV6HGwgCg==} + peerDependencies: + vuepress: 2.0.0-rc.9 + dependencies: + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/plugin-register-components@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-H500SYMlPgw85tE3Gcpfn5phlS6t+CeQo+Nj7epzfe0cDfZ9cnqwyM5KPNfd9NBl1Lan8JsEU9YTEmTxasujDw==} + /@vuepress/plugin-sitemap@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-MGj8m+2gajFQ6ZibgkjZFA/BLhwPncYIGJ1D2k934VnziQNHJC3hz4THhr8jN+xv5DbD/LhU1TTo2vqJQ3iGnQ==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/utils': 2.0.0-beta.62 - chokidar: 3.6.0 + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + sitemap: 7.1.1 + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/plugin-theme-data@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-q6XHIDnZcJ5W55TlynKrwHtHormZedEY5man9zT4hlZywr3vVBgToHztObNTqgn6CssFaW2BFXDlW17iyS2D2A==} + /@vuepress/plugin-theme-data@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-3eN68ada9+gdzCNzK6o6uRfpTUoUHXo8EUXRUBk2K9bCtb/dL20Q+BUE61iN9zeEidVcf02BBrqvgBrjEU5CGQ==} + peerDependencies: + vuepress: 2.0.0-rc.9 dependencies: '@vue/devtools-api': 6.6.1 - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - vue: 3.4.21(typescript@5.3.3) + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - - supports-color - typescript dev: true - /@vuepress/shared@2.0.0-beta.61: - resolution: {integrity: sha512-NhOQ1FDr5lDSu5IinNlNNzrF+jGOZ+bMFUyAlCxlTvK9oY6aRBCNwV8dWme+yoh3/zviKHGu62Xp7J2hKAHNZA==} + /@vuepress/shared@2.0.0-rc.2: + resolution: {integrity: sha512-2kmm0rw+WalRWrSC5pW0TXRz8Wyuh57XmOZEUOhPOflw4o8Dno+PcaWbdOZ/TLkTgTt3X1n7r1/c1ALtaLta8g==} dependencies: - '@mdit-vue/types': 0.12.0 - '@vue/shared': 3.4.21 + '@mdit-vue/types': 2.0.0 dev: true - /@vuepress/shared@2.0.0-beta.62: - resolution: {integrity: sha512-+OH8WzFz7+IUv+WbcBbCiy3ZTWZ4a2uVRd4GYHWkTE4Ux5V2Sx3KwY17POIGpn/PfMqNHHtjpDH6rO7qmaD+pg==} + /@vuepress/shared@2.0.0-rc.7: + resolution: {integrity: sha512-zNsYzAW5tuENb4vML0pK/61W3EscyOcn5JVPC+c6AwvsYWyAigZaMSu9ycGAptjDwzdYSi3gd33N9Q9T7pG77Q==} dependencies: - '@mdit-vue/types': 0.12.0 - '@vue/shared': 3.4.21 + '@mdit-vue/types': 2.0.0 dev: true - /@vuepress/shared@2.0.0-rc.2: - resolution: {integrity: sha512-2kmm0rw+WalRWrSC5pW0TXRz8Wyuh57XmOZEUOhPOflw4o8Dno+PcaWbdOZ/TLkTgTt3X1n7r1/c1ALtaLta8g==} + /@vuepress/shared@2.0.0-rc.9: + resolution: {integrity: sha512-XfI6CWNv4/Vp9Iew6GJil9RUSy1rM7zGdjwikr0j3Rkh55q3f00w1wud47wE9kxRqsZ0PIvsMget5CxEn5rA/w==} dependencies: '@mdit-vue/types': 2.0.0 dev: true - /@vuepress/theme-default@2.0.0-beta.62(typescript@5.3.3): - resolution: {integrity: sha512-J6wLH4tevMnn/2y+MrTpZEVDWf5yvikx0S9TIfpcxjR/nN4XD9eSZrMB3Lt8JqTW/lwFze5MVBdTtVafZs4b3g==} - dependencies: - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-active-header-links': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-back-to-top': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-container': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-external-link-icon': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-git': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-medium-zoom': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-nprogress': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-palette': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-prismjs': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/plugin-theme-data': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/shared': 2.0.0-beta.62 - '@vuepress/utils': 2.0.0-beta.62 - '@vueuse/core': 10.9.0(vue@3.4.21) - sass: 1.71.1 - sass-loader: 13.3.3(sass@1.71.1) - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) + /@vuepress/theme-default@2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-g0knQJpsCVGBRm6iGBMmW0uSlReAA42I9qt6fmt2sRlVjhjAniStTHoZUM4kzRGFSwqqi6BzMdp8RVXolkE/fQ==} + peerDependencies: + sass-loader: ^14.0.0 + vuepress: 2.0.0-rc.9 + peerDependenciesMeta: + sass-loader: + optional: true + dependencies: + '@vuepress/helper': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-active-header-links': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-back-to-top': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-container': 2.0.0-rc.25(vuepress@2.0.0-rc.9) + '@vuepress/plugin-copy-code': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-external-link-icon': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-git': 2.0.0-rc.22(vuepress@2.0.0-rc.9) + '@vuepress/plugin-links-check': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-medium-zoom': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-nprogress': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-palette': 2.0.0-rc.21(vuepress@2.0.0-rc.9) + '@vuepress/plugin-prismjs': 2.0.0-rc.21(vuepress@2.0.0-rc.9) + '@vuepress/plugin-seo': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-sitemap': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vuepress/plugin-theme-data': 2.0.0-rc.26(typescript@5.3.3)(vuepress@2.0.0-rc.9) + '@vueuse/core': 10.9.0(vue@3.4.26) + sass: 1.76.0 + vue: 3.4.26(typescript@5.3.3) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - '@vue/composition-api' - - fibers - - node-sass - - sass-embedded - - supports-color - typescript - - webpack dev: true - /@vuepress/utils@2.0.0-beta.61: - resolution: {integrity: sha512-W7g6xjrdyOW5E1V1ouyTm5d4+kgSd4KcM80D7K0NNScrhLIW6gpOggVVOVyTH3q2K1GQhzPlUcUe04ZNSo0ilQ==} + /@vuepress/utils@2.0.0-rc.2: + resolution: {integrity: sha512-g93yFJKtztpdXm4XyOIQ9QcUrKuvuWizvH3qWDQ5/WKlxa6VqE7nVNPlkudgGUIc7Bl4AGrlHcmgvkwaNoMcfA==} dependencies: '@types/debug': 4.1.12 '@types/fs-extra': 11.0.4 '@types/hash-sum': 1.0.2 - '@vuepress/shared': 2.0.0-beta.61 + '@vuepress/shared': 2.0.0-rc.2 debug: 4.3.4(supports-color@8.1.1) fs-extra: 11.2.0 - globby: 13.2.2 + globby: 14.0.1 hash-sum: 2.0.0 - ora: 6.3.1 + ora: 8.0.1 picocolors: 1.0.0 upath: 2.0.1 transitivePeerDependencies: - supports-color dev: true - /@vuepress/utils@2.0.0-beta.62: - resolution: {integrity: sha512-2hyGGrN1XCUapsSlckHc7FWkklSPZfqcM5eDYjxyIT9XpQrXKYn8r0CUVcveyFdHF76Tw0KyP57JCxUDTxHxVg==} + /@vuepress/utils@2.0.0-rc.7: + resolution: {integrity: sha512-47c7T72JwOWH1EgG7f/KwWNpIknd9IC5JmrizGI5IVUM4G16Vyj5oPQuXqLobV47m8JOxPW4dLpsglZGVaKQeg==} dependencies: '@types/debug': 4.1.12 '@types/fs-extra': 11.0.4 '@types/hash-sum': 1.0.2 - '@vuepress/shared': 2.0.0-beta.62 + '@vuepress/shared': 2.0.0-rc.7 debug: 4.3.4(supports-color@8.1.1) fs-extra: 11.2.0 - globby: 13.2.2 + globby: 14.0.1 hash-sum: 2.0.0 - ora: 6.3.1 + ora: 8.0.1 picocolors: 1.0.0 upath: 2.0.1 transitivePeerDependencies: - supports-color dev: true - /@vuepress/utils@2.0.0-rc.2: - resolution: {integrity: sha512-g93yFJKtztpdXm4XyOIQ9QcUrKuvuWizvH3qWDQ5/WKlxa6VqE7nVNPlkudgGUIc7Bl4AGrlHcmgvkwaNoMcfA==} + /@vuepress/utils@2.0.0-rc.9: + resolution: {integrity: sha512-qk6Pel4JVKYKxp3bWxyvnwchvx3QaCWc7SqUw7L6qUo/um+0U2U45L0anWoAfckw12RXYhoIEbJ9UZpueiKOPg==} dependencies: '@types/debug': 4.1.12 '@types/fs-extra': 11.0.4 '@types/hash-sum': 1.0.2 - '@vuepress/shared': 2.0.0-rc.2 + '@vuepress/shared': 2.0.0-rc.9 debug: 4.3.4(supports-color@8.1.1) fs-extra: 11.2.0 globby: 14.0.1 @@ -2494,6 +2945,18 @@ packages: - vue dev: true + /@vueuse/core@10.9.0(vue@3.4.26): + resolution: {integrity: sha512-/1vjTol8SXnx6xewDEKfS0Ra//ncg4Hb0DaZiwKf7drgfMsKFExQ+FnnENcN6efPen+1kIzhLQoGSy0eDUVOMg==} + dependencies: + '@types/web-bluetooth': 0.0.20 + '@vueuse/metadata': 10.9.0 + '@vueuse/shared': 10.9.0(vue@3.4.26) + vue-demi: 0.14.7(vue@3.4.26) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: true + /@vueuse/metadata@10.9.0: resolution: {integrity: sha512-iddNbg3yZM0X7qFY2sAotomgdHK7YJ6sKUvQqbvwnf7TmaVPxS4EJydcNsVejNdS8iWCtDk+fYXr7E32nyTnGA==} dev: true @@ -2507,6 +2970,15 @@ packages: - vue dev: true + /@vueuse/shared@10.9.0(vue@3.4.26): + resolution: {integrity: sha512-Uud2IWncmAfJvRaFYzv5OHDli+FbOzxiVEQdLCKQKLyhz94PIyFC3CHcH7EDMwIn8NPtD06+PNbC/PiO0LGLtw==} + dependencies: + vue-demi: 0.14.7(vue@3.4.26) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: true + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -2877,19 +3349,19 @@ packages: /async@3.2.5: resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==} - /autoprefixer@10.4.18(postcss@8.4.35): - resolution: {integrity: sha512-1DKbDfsr6KUElM6wg+0zRNkB/Q7WcKYAaK+pzXn+Xqmszm/5Xa9coeNdtP88Vi+dPzZnMjhge8GIV49ZQkDa+g==} + /autoprefixer@10.4.19(postcss@8.4.38): + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 dependencies: browserslist: 4.23.0 - caniuse-lite: 1.0.30001593 + caniuse-lite: 1.0.30001614 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.0.0 - postcss: 8.4.35 + postcss: 8.4.38 postcss-value-parser: 4.2.0 dev: true @@ -2962,6 +3434,7 @@ packages: /base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + dev: false /benchmark@2.1.4: resolution: {integrity: sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==} @@ -2983,14 +3456,6 @@ packages: readable-stream: 3.6.2 dev: false - /bl@5.1.0: - resolution: {integrity: sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==} - dependencies: - buffer: 6.0.3 - inherits: 2.0.4 - readable-stream: 3.6.2 - dev: true - /bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} dev: false @@ -3078,13 +3543,6 @@ packages: ieee754: 1.2.1 dev: false - /buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - dev: true - /builtin-status-codes@3.0.0: resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} dev: false @@ -3265,6 +3723,10 @@ packages: resolution: {integrity: sha512-UWM1zlo3cZfkpBysd7AS+z+v007q9G1+fLTUU42rQnY6t2axoogPW/xol6T7juU5EUoOhML4WgBIdG+9yYqAjQ==} dev: true + /caniuse-lite@1.0.30001614: + resolution: {integrity: sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==} + dev: true + /capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} dependencies: @@ -3606,6 +4068,7 @@ packages: /clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + dev: false /cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} @@ -4179,6 +4642,7 @@ packages: resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} dependencies: clone: 1.0.4 + dev: false /defer-to-connect@1.1.3: resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==} @@ -4395,11 +4859,6 @@ packages: strip-ansi: 6.0.1 dev: false - /entities@3.0.1: - resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} - engines: {node: '>=0.12'} - dev: true - /entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -4495,34 +4954,66 @@ packages: resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} dev: true - /esbuild@0.17.19: - resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + /esbuild@0.19.12: + resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/aix-ppc64': 0.19.12 + '@esbuild/android-arm': 0.19.12 + '@esbuild/android-arm64': 0.19.12 + '@esbuild/android-x64': 0.19.12 + '@esbuild/darwin-arm64': 0.19.12 + '@esbuild/darwin-x64': 0.19.12 + '@esbuild/freebsd-arm64': 0.19.12 + '@esbuild/freebsd-x64': 0.19.12 + '@esbuild/linux-arm': 0.19.12 + '@esbuild/linux-arm64': 0.19.12 + '@esbuild/linux-ia32': 0.19.12 + '@esbuild/linux-loong64': 0.19.12 + '@esbuild/linux-mips64el': 0.19.12 + '@esbuild/linux-ppc64': 0.19.12 + '@esbuild/linux-riscv64': 0.19.12 + '@esbuild/linux-s390x': 0.19.12 + '@esbuild/linux-x64': 0.19.12 + '@esbuild/netbsd-x64': 0.19.12 + '@esbuild/openbsd-x64': 0.19.12 + '@esbuild/sunos-x64': 0.19.12 + '@esbuild/win32-arm64': 0.19.12 + '@esbuild/win32-ia32': 0.19.12 + '@esbuild/win32-x64': 0.19.12 + dev: true + + /esbuild@0.20.2: + resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} hasBin: true requiresBuild: true optionalDependencies: - '@esbuild/android-arm': 0.17.19 - '@esbuild/android-arm64': 0.17.19 - '@esbuild/android-x64': 0.17.19 - '@esbuild/darwin-arm64': 0.17.19 - '@esbuild/darwin-x64': 0.17.19 - '@esbuild/freebsd-arm64': 0.17.19 - '@esbuild/freebsd-x64': 0.17.19 - '@esbuild/linux-arm': 0.17.19 - '@esbuild/linux-arm64': 0.17.19 - '@esbuild/linux-ia32': 0.17.19 - '@esbuild/linux-loong64': 0.17.19 - '@esbuild/linux-mips64el': 0.17.19 - '@esbuild/linux-ppc64': 0.17.19 - '@esbuild/linux-riscv64': 0.17.19 - '@esbuild/linux-s390x': 0.17.19 - '@esbuild/linux-x64': 0.17.19 - '@esbuild/netbsd-x64': 0.17.19 - '@esbuild/openbsd-x64': 0.17.19 - '@esbuild/sunos-x64': 0.17.19 - '@esbuild/win32-arm64': 0.17.19 - '@esbuild/win32-ia32': 0.17.19 - '@esbuild/win32-x64': 0.17.19 + '@esbuild/aix-ppc64': 0.20.2 + '@esbuild/android-arm': 0.20.2 + '@esbuild/android-arm64': 0.20.2 + '@esbuild/android-x64': 0.20.2 + '@esbuild/darwin-arm64': 0.20.2 + '@esbuild/darwin-x64': 0.20.2 + '@esbuild/freebsd-arm64': 0.20.2 + '@esbuild/freebsd-x64': 0.20.2 + '@esbuild/linux-arm': 0.20.2 + '@esbuild/linux-arm64': 0.20.2 + '@esbuild/linux-ia32': 0.20.2 + '@esbuild/linux-loong64': 0.20.2 + '@esbuild/linux-mips64el': 0.20.2 + '@esbuild/linux-ppc64': 0.20.2 + '@esbuild/linux-riscv64': 0.20.2 + '@esbuild/linux-s390x': 0.20.2 + '@esbuild/linux-x64': 0.20.2 + '@esbuild/netbsd-x64': 0.20.2 + '@esbuild/openbsd-x64': 0.20.2 + '@esbuild/sunos-x64': 0.20.2 + '@esbuild/win32-arm64': 0.20.2 + '@esbuild/win32-ia32': 0.20.2 + '@esbuild/win32-x64': 0.20.2 dev: true /escalade@3.1.2: @@ -4821,8 +5312,8 @@ packages: tmp: 0.0.33 dev: true - /fflate@0.7.4: - resolution: {integrity: sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==} + /fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} dev: true /figures@1.7.0: @@ -5567,6 +6058,7 @@ packages: /ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + dev: false /ignore-by-default@2.1.0: resolution: {integrity: sha512-yiWd4GVmJp0Q6ghmM2B/V3oZGRmjrKLXvHR3TE1nfoXsmoggllfZUQe74EN0fJdPFZu2NIvNdrMMLm3OsV7Ohw==} @@ -6387,12 +6879,6 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true - /linkify-it@4.0.1: - resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} - dependencies: - uc.micro: 1.0.6 - dev: true - /linkify-it@5.0.0: resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} dependencies: @@ -6643,14 +7129,6 @@ packages: chalk: 4.1.2 is-unicode-supported: 0.1.0 - /log-symbols@5.1.0: - resolution: {integrity: sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA==} - engines: {node: '>=12'} - dependencies: - chalk: 5.3.0 - is-unicode-supported: 1.3.0 - dev: true - /log-symbols@6.0.0: resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} engines: {node: '>=18'} @@ -6738,6 +7216,12 @@ packages: dependencies: yallist: 4.0.0 + /magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} @@ -6782,51 +7266,48 @@ packages: resolution: {integrity: sha512-BbShUnr5OartXJe1GeccAWtfro11hhgNJg6G9/UtWKjVGvV5U4C09cg5nk8JUevhXODaXY+hQ3xxMUKSs62ONQ==} dev: false - /markdown-it-anchor@8.6.7(@types/markdown-it@12.2.3)(markdown-it@13.0.2): + /markdown-it-anchor@8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.0.0): resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} peerDependencies: '@types/markdown-it': '*' markdown-it: '*' dependencies: - '@types/markdown-it': 12.2.3 - markdown-it: 13.0.2 + '@types/markdown-it': 13.0.7 + markdown-it: 14.0.0 dev: true - /markdown-it-anchor@8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.0.0): + /markdown-it-anchor@8.6.7(@types/markdown-it@13.0.7)(markdown-it@14.1.0): resolution: {integrity: sha512-FlCHFwNnutLgVTflOYHPW2pPcl2AACqVzExlkGQNsi4CJgqOHN7YTgDd4LuhgN1BFO3TS0vLAruV1Td6dwWPJA==} peerDependencies: '@types/markdown-it': '*' markdown-it: '*' dependencies: '@types/markdown-it': 13.0.7 - markdown-it: 14.0.0 - dev: true - - /markdown-it-container@3.0.0: - resolution: {integrity: sha512-y6oKTq4BB9OQuY/KLfk/O3ysFhB3IMYoIWhGJEidXt1NQFocFK2sA2t0NYZAMyMShAGL6x5OPIbrmXPIqaN9rw==} + markdown-it: 14.1.0 dev: true - /markdown-it-emoji@2.0.2: - resolution: {integrity: sha512-zLftSaNrKuYl0kR5zm4gxXjHaOI3FAOEaloKmRA5hijmJZvSjmxcokOLlzycb/HXlUFWzXqpIEoyEMCE4i9MvQ==} + /markdown-it-container@4.0.0: + resolution: {integrity: sha512-HaNccxUH0l7BNGYbFbjmGpf5aLHAMTinqRZQAEQbMr2cdD3z91Q6kIo1oUn1CQndkT03jat6ckrdRYuwwqLlQw==} dev: true /markdown-it-emoji@3.0.0: resolution: {integrity: sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==} dev: true - /markdown-it@13.0.2: - resolution: {integrity: sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==} + /markdown-it@14.0.0: + resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==} hasBin: true dependencies: argparse: 2.0.1 - entities: 3.0.1 - linkify-it: 4.0.1 - mdurl: 1.0.1 - uc.micro: 1.0.6 + entities: 4.5.0 + linkify-it: 5.0.0 + mdurl: 2.0.0 + punycode.js: 2.3.1 + uc.micro: 2.1.0 dev: true - /markdown-it@14.0.0: - resolution: {integrity: sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==} + /markdown-it@14.1.0: + resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true dependencies: argparse: 2.0.1 @@ -6851,10 +7332,6 @@ packages: blueimp-md5: 2.19.0 dev: true - /mdurl@1.0.1: - resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} - dev: true - /mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} dev: true @@ -7599,21 +8076,6 @@ packages: wcwidth: 1.0.1 dev: false - /ora@6.3.1: - resolution: {integrity: sha512-ERAyNnZOfqM+Ao3RAvIXkYh5joP220yf59gVe2X/cI6SiCxIdi4c9HZKZD8R6q/RDXEje1THBju6iExiSsgJaQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - chalk: 5.3.0 - cli-cursor: 4.0.0 - cli-spinners: 2.9.2 - is-interactive: 2.0.0 - is-unicode-supported: 1.3.0 - log-symbols: 5.1.0 - stdin-discarder: 0.1.0 - strip-ansi: 7.1.0 - wcwidth: 1.0.1 - dev: true - /ora@8.0.1: resolution: {integrity: sha512-ANIvzobt1rls2BDny5fWZ3ZVKyD6nscLvfFRpQgfWsythlcsVUC9kL0zq6j2Z5z9wwp1kd7wpsD/T9qNPVLCaQ==} engines: {node: '>=18'} @@ -8077,21 +8539,20 @@ packages: engines: {node: '>= 0.4'} dev: true - /postcss-load-config@4.0.2(postcss@8.4.35)(ts-node@10.9.2): - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + /postcss-load-config@5.0.3(postcss@8.4.38): + resolution: {integrity: sha512-90pBBI5apUVruIEdCxZic93Wm+i9fTrp7TXbgdUCH+/L+2WnfpITSpq5dFU/IPvbv7aNiMlQISpUkAm3fEcvgQ==} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' peerDependenciesMeta: - postcss: + jiti: optional: true - ts-node: + postcss: optional: true dependencies: lilconfig: 3.1.1 - postcss: 8.4.35 - ts-node: 10.9.2(@types/node@18.19.21)(typescript@5.3.3) + postcss: 8.4.38 yaml: 2.4.0 dev: true @@ -8108,6 +8569,15 @@ packages: source-map-js: 1.0.2 dev: true + /postcss@8.4.38: + resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.2.0 + dev: true + /preact@10.19.6: resolution: {integrity: sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==} dev: true @@ -8500,11 +8970,29 @@ packages: glob: 9.3.5 dev: false - /rollup@3.29.4: - resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} - engines: {node: '>=14.18.0', npm: '>=8.0.0'} + /rollup@4.17.2: + resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + dependencies: + '@types/estree': 1.0.5 optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.17.2 + '@rollup/rollup-android-arm64': 4.17.2 + '@rollup/rollup-darwin-arm64': 4.17.2 + '@rollup/rollup-darwin-x64': 4.17.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.17.2 + '@rollup/rollup-linux-arm-musleabihf': 4.17.2 + '@rollup/rollup-linux-arm64-gnu': 4.17.2 + '@rollup/rollup-linux-arm64-musl': 4.17.2 + '@rollup/rollup-linux-powerpc64le-gnu': 4.17.2 + '@rollup/rollup-linux-riscv64-gnu': 4.17.2 + '@rollup/rollup-linux-s390x-gnu': 4.17.2 + '@rollup/rollup-linux-x64-gnu': 4.17.2 + '@rollup/rollup-linux-x64-musl': 4.17.2 + '@rollup/rollup-win32-arm64-msvc': 4.17.2 + '@rollup/rollup-win32-ia32-msvc': 4.17.2 + '@rollup/rollup-win32-x64-msvc': 4.17.2 fsevents: 2.3.3 dev: true @@ -8562,31 +9050,18 @@ packages: /safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - /sass-loader@13.3.3(sass@1.71.1): - resolution: {integrity: sha512-mt5YN2F1MOZr3d/wBRcZxeFgwgkH44wVc2zohO2YF6JiOMkiXe4BYRZpSu2sO1g71mo/j16txzUhsKZlqjVGzA==} - engines: {node: '>= 14.15.0'} - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 - sass: ^1.3.0 - sass-embedded: '*' - webpack: ^5.0.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true - sass-embedded: - optional: true + /sass@1.71.1: + resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==} + engines: {node: '>=14.0.0'} + hasBin: true dependencies: - neo-async: 2.6.2 - sass: 1.71.1 + chokidar: 3.6.0 + immutable: 4.3.5 + source-map-js: 1.0.2 dev: true - /sass@1.71.1: - resolution: {integrity: sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==} + /sass@1.76.0: + resolution: {integrity: sha512-nc3LeqvF2FNW5xGF1zxZifdW3ffIz5aBb7I7tSvOoNu7z1RQ6pFt9MBuiPtjgaI62YWrM/txjWlOCFiGtf2xpw==} engines: {node: '>=14.0.0'} hasBin: true dependencies: @@ -8838,6 +9313,11 @@ packages: engines: {node: '>=0.10.0'} dev: true + /source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} dependencies: @@ -8926,13 +9406,6 @@ packages: engines: {node: '>= 0.6'} dev: false - /stdin-discarder@0.1.0: - resolution: {integrity: sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dependencies: - bl: 5.1.0 - dev: true - /stdin-discarder@0.2.2: resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} engines: {node: '>=18'} @@ -9546,10 +10019,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - /uc.micro@1.0.6: - resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} - dev: true - /uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} dev: true @@ -9767,13 +10236,14 @@ packages: builtins: 5.0.1 dev: false - /vite@4.3.9(@types/node@18.19.21)(sass@1.71.1): - resolution: {integrity: sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==} - engines: {node: ^14.18.0 || >=16.0.0} + /vite@5.0.13(@types/node@18.19.21)(sass@1.71.1): + resolution: {integrity: sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@types/node': '>= 14' + '@types/node': ^18.0.0 || >=20.0.0 less: '*' + lightningcss: ^1.21.0 sass: '*' stylus: '*' sugarss: '*' @@ -9783,6 +10253,8 @@ packages: optional: true less: optional: true + lightningcss: + optional: true sass: optional: true stylus: @@ -9793,9 +10265,9 @@ packages: optional: true dependencies: '@types/node': 18.19.21 - esbuild: 0.17.19 - postcss: 8.4.35 - rollup: 3.29.4 + esbuild: 0.19.12 + postcss: 8.4.38 + rollup: 4.17.2 sass: 1.71.1 optionalDependencies: fsevents: 2.3.3 @@ -9816,6 +10288,21 @@ packages: vue: 3.4.21(typescript@5.3.3) dev: true + /vue-demi@0.14.7(vue@3.4.26): + resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.4.26(typescript@5.3.3) + dev: true + /vue-router@4.3.0(vue@3.4.21): resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==} peerDependencies: @@ -9825,6 +10312,15 @@ packages: vue: 3.4.21(typescript@5.3.3) dev: true + /vue-router@4.3.0(vue@3.4.26): + resolution: {integrity: sha512-dqUcs8tUeG+ssgWhcPbjHvazML16Oga5w34uCUmsk7i0BcnskoLGwjpa15fqMr2Fa5JgVBrdL2MEgqz6XZ/6IQ==} + peerDependencies: + vue: ^3.2.0 + dependencies: + '@vue/devtools-api': 6.6.1 + vue: 3.4.26(typescript@5.3.3) + dev: true + /vue@3.4.21(typescript@5.3.3): resolution: {integrity: sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==} peerDependencies: @@ -9841,29 +10337,34 @@ packages: typescript: 5.3.3 dev: true - /vuepress-plugin-sitemap2@2.0.0-beta.205(typescript@5.3.3)(vuepress@2.0.0-beta.62): - resolution: {integrity: sha512-suYQEYPnH0Ytqytq1oK0YTeh4ZC24FwPCGgMefg/jjXLa5fTSWVvOHzGWqfNCmnYzEiU52Txgju/RAbIFHmeHA==} - engines: {node: ^14.18.0 || >=16.0.0, npm: '>=8', pnpm: '>=7'} + /vue@3.4.26(typescript@5.3.3): + resolution: {integrity: sha512-bUIq/p+VB+0xrJubaemrfhk1/FiW9iX+pDV+62I/XJ6EkspAO9/DXEjbDFoe8pIfOZBqfk45i9BMc41ptP/uRg==} peerDependencies: - vuepress: 2.0.0-beta.61 - vuepress-vite: 2.0.0-beta.61 - vuepress-webpack: 2.0.0-beta.61 + typescript: '*' peerDependenciesMeta: - vuepress: - optional: true - vuepress-vite: - optional: true - vuepress-webpack: + typescript: optional: true dependencies: - '@vuepress/shared': 2.0.0-beta.61 - '@vuepress/utils': 2.0.0-beta.61 + '@vue/compiler-dom': 3.4.26 + '@vue/compiler-sfc': 3.4.26 + '@vue/runtime-dom': 3.4.26 + '@vue/server-renderer': 3.4.26(vue@3.4.26) + '@vue/shared': 3.4.26 + typescript: 5.3.3 + dev: true + + /vuepress-plugin-sitemap2@2.0.0-rc.16(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-7hGlrwACCSZijuGFyc4Yh+3IXh8f9dFK0iekjlMbC2TxNbDHHmsLHnsGfEmd6H1xsQtaTC1fwXw158jiXwE1fA==} + engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} + deprecated: Please use @vuepress/plugin-sitemap@v2 instead + peerDependencies: + vuepress: 2.0.0-rc.2 + dependencies: sitemap: 7.1.1 - vuepress: 2.0.0-beta.62(@types/node@18.19.21)(@vuepress/client@2.0.0-beta.62)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3)(vue@3.4.21) - vuepress-shared: 2.0.0-beta.205(typescript@5.3.3)(vuepress@2.0.0-beta.62) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) + vuepress-shared: 2.0.0-rc.16(typescript@5.3.3)(vuepress@2.0.0-rc.9) transitivePeerDependencies: - '@vue/composition-api' - - supports-color - typescript dev: true @@ -9880,101 +10381,60 @@ packages: - typescript dev: true - /vuepress-shared@2.0.0-beta.205(typescript@5.3.3)(vuepress@2.0.0-beta.62): - resolution: {integrity: sha512-DcfAsWZg9NstiYmagMeLEqqRMYAgSCA3GJtWRN3vVCZDUWRG7j64xKSsO3HlbGOtNST74O3HHEqoGu2pyRGQ1Q==} - engines: {node: ^14.18.0 || >=16.0.0, npm: '>=8', pnpm: '>=7'} + /vuepress-shared@2.0.0-rc.16(typescript@5.3.3)(vuepress@2.0.0-rc.9): + resolution: {integrity: sha512-gsiqo9tr6dHCVQTPw1d+oiJyNGzc6nmrGRBWkLb3ZxD15q1k/iv2flBwPdb1RasU827oMgZ2DuOzbHcGjRKjSA==} + engines: {node: '>=18.16.0', npm: '>=8', pnpm: '>=7', yarn: '>=2'} peerDependencies: - vuepress: 2.0.0-beta.61 - vuepress-vite: 2.0.0-beta.61 - vuepress-webpack: 2.0.0-beta.61 - peerDependenciesMeta: - vuepress: - optional: true - vuepress-vite: - optional: true - vuepress-webpack: - optional: true + vuepress: 2.0.0-rc.2 dependencies: - '@vuepress/client': 2.0.0-beta.61(typescript@5.3.3) - '@vuepress/shared': 2.0.0-beta.61 - '@vuepress/utils': 2.0.0-beta.61 - '@vueuse/core': 10.9.0(vue@3.4.21) + '@vueuse/core': 10.9.0(vue@3.4.26) cheerio: 1.0.0-rc.12 dayjs: 1.11.10 - execa: 7.2.0 - fflate: 0.7.4 + execa: 8.0.1 + fflate: 0.8.2 gray-matter: 4.0.3 semver: 7.6.0 striptags: 3.2.0 - vue: 3.4.21(typescript@5.3.3) - vue-router: 4.3.0(vue@3.4.21) - vuepress: 2.0.0-beta.62(@types/node@18.19.21)(@vuepress/client@2.0.0-beta.62)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3)(vue@3.4.21) + vue: 3.4.26(typescript@5.3.3) + vue-router: 4.3.0(vue@3.4.26) + vuepress: 2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21) transitivePeerDependencies: - '@vue/composition-api' - - supports-color - typescript dev: true - /vuepress-vite@2.0.0-beta.62(@types/node@18.19.21)(@vuepress/client@2.0.0-beta.62)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3)(vue@3.4.21): - resolution: {integrity: sha512-C93T5ZCFMnbdXkZ/R/romtwPPP2zjPN38YZhrM6w6wWjSMDvrG26IFRwluXy+W84O0Pg7xOwqRom0wvO4kCxmA==} - engines: {node: '>=16.19.0'} + /vuepress@2.0.0-rc.9(@vuepress/bundler-vite@2.0.0-rc.7)(typescript@5.3.3)(vue@3.4.21): + resolution: {integrity: sha512-jT1ln2lawdph+vVI6n2JfEUhQIcyc1RQWDdQu9DffhJGywJunFcumnUJudpqd1SNIES2Fz1hVCD6gdrE/rVKOQ==} + engines: {node: '>=18.16.0'} hasBin: true peerDependencies: - '@vuepress/client': 2.0.0-beta.62 - vue: ^3.3.1 - dependencies: - '@vuepress/bundler-vite': 2.0.0-beta.62(@types/node@18.19.21)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3) - '@vuepress/cli': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/client': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/core': 2.0.0-beta.62(typescript@5.3.3) - '@vuepress/theme-default': 2.0.0-beta.62(typescript@5.3.3) - vue: 3.4.21(typescript@5.3.3) - transitivePeerDependencies: - - '@types/node' - - '@vue/composition-api' - - fibers - - less - - node-sass - - sass - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - ts-node - - typescript - - webpack - dev: true - - /vuepress@2.0.0-beta.62(@types/node@18.19.21)(@vuepress/client@2.0.0-beta.62)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3)(vue@3.4.21): - resolution: {integrity: sha512-kwoC7RA6PGetWSU/NwV6dJ3VItg+R+K2IpAJ4bKsnRueIqGpDZwPr423nRK0VwDhh2sN7lUn6LoyaybPwWrGZg==} - engines: {node: '>=16.19.0'} - hasBin: true + '@vuepress/bundler-vite': 2.0.0-rc.9 + '@vuepress/bundler-webpack': 2.0.0-rc.9 + vue: ^3.4.0 + peerDependenciesMeta: + '@vuepress/bundler-vite': + optional: true + '@vuepress/bundler-webpack': + optional: true dependencies: - vuepress-vite: 2.0.0-beta.62(@types/node@18.19.21)(@vuepress/client@2.0.0-beta.62)(sass@1.71.1)(ts-node@10.9.2)(typescript@5.3.3)(vue@3.4.21) + '@vuepress/bundler-vite': 2.0.0-rc.7(@types/node@18.19.21)(sass@1.71.1)(typescript@5.3.3) + '@vuepress/cli': 2.0.0-rc.9(typescript@5.3.3) + '@vuepress/client': 2.0.0-rc.9(typescript@5.3.3) + '@vuepress/core': 2.0.0-rc.9(typescript@5.3.3) + '@vuepress/markdown': 2.0.0-rc.9 + '@vuepress/shared': 2.0.0-rc.9 + '@vuepress/utils': 2.0.0-rc.9 + vue: 3.4.21(typescript@5.3.3) transitivePeerDependencies: - - '@types/node' - - '@vue/composition-api' - - '@vuepress/client' - - fibers - - less - - node-sass - - sass - - sass-embedded - - stylus - - sugarss - supports-color - - terser - - ts-node - typescript - - vue - - webpack dev: true /wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} dependencies: defaults: 1.0.4 + dev: false /well-known-symbols@2.0.0: resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==} diff --git a/src/generator/__tests__/__snapshots__/json-template.test.ts.md b/src/generator/__tests__/__snapshots__/json-template.test.ts.md new file mode 100644 index 000000000..79c4efd0a --- /dev/null +++ b/src/generator/__tests__/__snapshots__/json-template.test.ts.md @@ -0,0 +1,15 @@ +# Snapshot report for `src/generator/__tests__/json-template.test.ts` + +The actual snapshot is saved in `json-template.test.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## render + +> Snapshot 1 + + `{␊ + "foo": "foo",␊ + "bar": "bar",␊ + "outbounds": "new-value"␊ + }` diff --git a/src/generator/__tests__/__snapshots__/json-template.test.ts.snap b/src/generator/__tests__/__snapshots__/json-template.test.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..b86ed8d3dd5ac4f0bc1105d66217d2b3c858bc21 GIT binary patch literal 169 zcmZ<^b5sbM}ip>N4ZJ3_VQrx5jMO UFdH?_b literal 0 HcmV?d00001 diff --git a/src/generator/__tests__/json-template.test.ts b/src/generator/__tests__/json-template.test.ts new file mode 100644 index 000000000..357380888 --- /dev/null +++ b/src/generator/__tests__/json-template.test.ts @@ -0,0 +1,207 @@ +import test from 'ava' +import sinon from 'sinon' +import fs from 'fs-extra' + +import { + extendOutbounds, + createExtendFunction, + combineExtendFunctions, + render, +} from '../json-template' + +test.beforeEach(() => { + sinon.restore() +}) + +test('extendOutbounds - extend string', (t) => { + const extend = extendOutbounds('new-value') + + t.deepEqual( + extend({ + foo: 'foo', + }), + { + foo: 'foo', + outbounds: 'new-value', + }, + ) +}) + +test('extendOutbounds - extend object', (t) => { + const extend = extendOutbounds({ + bar: 'bar', + }) + + t.deepEqual( + extend({ + foo: 'foo', + }), + { + foo: 'foo', + outbounds: { + bar: 'bar', + }, + }, + ) +}) + +test('extendOutbounds - extend array', (t) => { + const extendString = extendOutbounds('new-value') + t.deepEqual( + extendString({ + foo: 'foo', + outbounds: ['old-value'], + }), + { + foo: 'foo', + outbounds: ['old-value', 'new-value'], + }, + ) + + const extendObject = extendOutbounds({ + bar: 'bar', + }) + t.deepEqual( + extendObject({ + outbounds: [ + { + foo: 'foo', + }, + ], + }), + { + outbounds: [ + { + foo: 'foo', + }, + { + bar: 'bar', + }, + ], + }, + ) + + const extendArray = extendOutbounds(['new-value1', 'new-value2']) + + t.deepEqual(extendArray({ outbounds: ['old-value'] }), { + outbounds: ['old-value', 'new-value1', 'new-value2'], + }) +}) + +test('extendOutbounds - extend function that returns object', (t) => { + const spy = sinon.spy(() => { + t.pass() + return 'something' + }) + const extend = extendOutbounds(({ getSomething }) => { + return { + bar: getSomething(), + } + }) + + t.deepEqual( + extend( + { + foo: 'foo', + }, + { + getSomething: spy, + }, + ), + { + foo: 'foo', + outbounds: { + bar: 'something', + }, + }, + ) + t.is(spy.calledOnce, true) +}) + +test('createExtendFunction - deep extend', (t) => { + const extendFunction = createExtendFunction('foo.bar') + const extend = extendFunction({ + bar: 'bar', + }) + + t.deepEqual( + extend({ + foo: { + baz: 'baz', + }, + }), + { + foo: { + baz: 'baz', + bar: { + bar: 'bar', + }, + }, + }, + ) +}) + +test('createExtendFunction - deep extend array', (t) => { + const extendFunction = createExtendFunction('foo[0]') + const extend = extendFunction({ + bar: 'bar', + }) + + t.deepEqual( + extend({ + foo: [ + { + baz: 'baz', + }, + ], + }), + { + foo: [ + { + bar: 'bar', + }, + ], + }, + ) +}) + +test('combineExtendFunctions', (t) => { + const extend1 = createExtendFunction('foo')({ bar: 'bar' }) + const extend2 = createExtendFunction('baz')({ qux: 'qux' }) + + const combined = combineExtendFunctions(extend1, extend2) + + t.deepEqual( + combined({ + original: 'original', + }), + { + original: 'original', + foo: { + bar: 'bar', + }, + baz: { + qux: 'qux', + }, + }, + ) +}) + +test('render', (t) => { + sinon.stub(fs, 'readJsonSync').returns({ + foo: 'foo', + bar: 'bar', + }) + + const result = render( + 'templateDir', + 'fileName', + extendOutbounds('new-value'), + { baz: 'baz' }, + ) + + t.snapshot(result) + t.true( + (fs.readJsonSync as sinon.SinonStub).calledOnceWith('templateDir/fileName'), + ) +}) diff --git a/src/generator/artifact.ts b/src/generator/artifact.ts index 0be14712e..0cec6f0c2 100644 --- a/src/generator/artifact.ts +++ b/src/generator/artifact.ts @@ -14,6 +14,7 @@ import { } from '../provider' import { ArtifactConfig, + ArtifactConfigInput, CommandConfig, NodeTypeEnum, PossibleNodeConfigType, @@ -49,12 +50,13 @@ import { getNetworkConcurrency, getSingboxNodeNames, getSingboxNodes, - getSingboxNodesString, } from '../utils' import { resolveDomain } from '../utils/dns' import { internalFilters, validateFilter } from '../filters' import { prependFlag, removeFlag } from '../utils/flag' +import { ArtifactValidator } from '../validators' import { loadLocalSnippet } from './template' +import { render as renderJSON } from './json-template' export interface ArtifactOptions { readonly remoteSnippetList?: ReadonlyArray @@ -65,6 +67,7 @@ export type ExtendableRenderContext = Record export class Artifact extends EventEmitter { public initProgress = 0 + public artifact: ArtifactConfig public providerNameList: ReadonlyArray public nodeConfigListMap: Map> = @@ -81,13 +84,15 @@ export class Artifact extends EventEmitter { constructor( public surgioConfig: CommandConfig, - public artifact: ArtifactConfig, + artifactConfig: ArtifactConfigInput, private options: ArtifactOptions = {}, ) { super() - const mainProviderName = artifact.provider - const combineProviders = artifact.combineProviders || [] + this.artifact = ArtifactValidator.parse(artifactConfig) + + const mainProviderName = this.artifact.provider + const combineProviders = this.artifact.combineProviders || [] this.providerNameList = [mainProviderName].concat(combineProviders) } @@ -132,7 +137,6 @@ export class Artifact extends EventEmitter { getNodeNames, getClashNodes, getClashNodeNames, - getSingboxNodesString, getSingboxNodes, getSingboxNodeNames, getSurgeNodes, @@ -227,17 +231,31 @@ export class Artifact extends EventEmitter { throw new Error('没有可用的 Nunjucks 环境') } + if ( + this.artifact.templateType === 'json' && + !this.artifact.extendTemplate + ) { + throw new Error('JSON 模板需要提供 extendTemplate 函数') + } + const renderContext = this.getRenderContext(extendRenderContext) - const { templateString, template } = this.artifact + const { templateString, template, templateType } = this.artifact const result = templateString ? targetTemplateEngine.renderString(templateString, { templateEngine: targetTemplateEngine, ...renderContext, }) - : targetTemplateEngine.render(`${template}.tpl`, { + : templateType === 'default' + ? targetTemplateEngine.render(`${template}.tpl`, { templateEngine: targetTemplateEngine, ...renderContext, }) + : renderJSON( + this.surgioConfig.templateDir, + `${template}.json`, + this.artifact.extendTemplate!, + renderContext, + ) this.emit('renderArtifact', { artifact: this.artifact, result }) @@ -443,7 +461,7 @@ export class Artifact extends EventEmitter { nodeConfig.underlyingProxy = provider.config.underlyingProxy } - // check whether the hostname resolves in case of blocking clash's node heurestic + // Check whether the hostname resolves in case of blocking clash's node heurestic if ( config?.checkHostname && 'hostname' in nodeConfig && diff --git a/src/generator/index.ts b/src/generator/index.ts index 0c650eec0..cc603747e 100644 --- a/src/generator/index.ts +++ b/src/generator/index.ts @@ -1,2 +1,7 @@ export * from './artifact' export * from './template' +export { + extendOutbounds, + createExtendFunction, + combineExtendFunctions, +} from './json-template' diff --git a/src/generator/json-template.ts b/src/generator/json-template.ts new file mode 100644 index 000000000..85f1d52b5 --- /dev/null +++ b/src/generator/json-template.ts @@ -0,0 +1,90 @@ +import { JsonObject } from 'type-fest' +import _ from 'lodash' +import fs from 'fs-extra' +import { join } from 'path' + +type ExtendContext = Record + +type PremitiveValue = string | number | boolean +type ExtendValue = + | JsonObject[] + | JsonObject + | PremitiveValue[] + | PremitiveValue + | (( + extendContext: ExtendContext, + ) => JsonObject[] | JsonObject | PremitiveValue[] | PremitiveValue) + +type ExtendFunction = ( + extendValue: ExtendValue, +) => (jsonInput: JsonObject, extendContext?: ExtendContext) => JsonObject + +export const createExtendFunction = (extendKey: string) => { + const extendFunction: ExtendFunction = (extendValue) => { + return (jsonInput, extendContext = {}) => { + const jsonInputCopy = _.cloneDeep(jsonInput) + const isExist = _.get(jsonInputCopy, extendKey) + const extendValueIsFunction = _.isFunction(extendValue) + const valueToExtend = extendValueIsFunction + ? extendValue(extendContext) + : extendValue + + if (isExist) { + if (_.isArray(isExist)) { + if (_.isArray(valueToExtend)) { + _.set(jsonInputCopy, extendKey, [...isExist, ...valueToExtend]) + } else { + _.set(jsonInputCopy, extendKey, [...isExist, valueToExtend]) + } + } else { + _.set(jsonInputCopy, extendKey, valueToExtend) + } + } else { + _.set(jsonInputCopy, extendKey, valueToExtend) + } + + return jsonInputCopy + } + } + + return extendFunction +} + +export const extendOutbounds = createExtendFunction('outbounds') + +export const combineExtendFunctions = ( + ...args: ReturnType[] +): ReturnType => { + return (jsonInput, extendContext = {}) => { + return args.reduce((acc, extend) => extend(acc, extendContext), jsonInput) + } +} + +export const render = ( + templateDir: string, + fileName: string, + extend: (...args: any[]) => any, + extendContext: ExtendContext, +): string => { + const templatePath = join(templateDir, fileName) + const jsonInput = fs.readJsonSync(templatePath) + + try { + const jsonOutput = (extend as ReturnType)( + jsonInput, + extendContext, + ) + + return JSON.stringify(jsonOutput, null, 2) + } catch (error) { + // istanbul ignore next + if (error instanceof Error) { + throw new Error( + `Error when rendering JSON template ${fileName}: ${error.message}`, + ) + } + + // istanbul ignore next + throw new Error(`Error when rendering JSON template ${fileName}`) + } +} diff --git a/src/index.ts b/src/index.ts index 050375984..fe46ba3fc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,6 +18,7 @@ export type { CommandConfigBeforeNormalize as SurgioConfig } from './types' export * from './configurables' export { default as httpClient } from './utils/http-client' export { unifiedCache as cache } from './utils/cache' +export { createExtendFunction, extendOutbounds } from './generator' const { internalFilters, ...filtersUtils } = filters diff --git a/src/types.ts b/src/types.ts index f8b34ea50..095461ad1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -50,9 +50,11 @@ export enum SupportProviderEnum { Trojan = 'trojan', } -export type CommandConfigBeforeNormalize = z.infer +export type CommandConfigBeforeNormalize = z.input -export type CommandConfig = CommandConfigBeforeNormalize & { +export type CommandConfigAfterNormalize = z.infer + +export type CommandConfig = CommandConfigAfterNormalize & { publicUrl: string output: string urlBase: string @@ -70,6 +72,8 @@ export interface RemoteSnippet extends RemoteSnippetConfig { export type ArtifactConfig = z.infer +export type ArtifactConfigInput = z.input + export type ProviderConfig = z.infer export interface BlackSSLProviderConfig extends ProviderConfig { diff --git a/src/utils/__tests__/singbox.test.ts b/src/utils/__tests__/singbox.test.ts index 9649d6d46..570e45158 100644 --- a/src/utils/__tests__/singbox.test.ts +++ b/src/utils/__tests__/singbox.test.ts @@ -579,10 +579,6 @@ const nodeList: ReadonlyArray = [ }, ] -// test('generateExpectedNodes', async (t) => { -// t.log(`[${singbox.getSingboxNodesString(nodeList)}]`) -// t.pass() -// }) const expectedNodes: Record[] = [ { type: 'shadowsocks', @@ -1022,10 +1018,7 @@ const expectedNodeNames = expectedNodes test('getSingboxNodeNames', async (t) => { t.deepEqual(singbox.getSingboxNodeNames(nodeList), expectedNodeNames) - t.deepEqual(singbox.getSingboxNodeNames(nodeList, undefined, ['TEST']), [ - 'TEST', - ...expectedNodeNames, - ]) + t.deepEqual( singbox.getSingboxNodeNames( nodeList, @@ -1033,20 +1026,4 @@ test('getSingboxNodeNames', async (t) => { ), ['ss'], ) - t.deepEqual( - singbox.getSingboxNodeNames( - nodeList, - (nodeConfig) => nodeConfig.nodeName === 'non-exist', - [], - ['foo'], - ), - ['foo'], - ) -}) - -test('getSingboxNodesString', async (t) => { - t.deepEqual( - JSON.parse(`[${singbox.getSingboxNodesString(nodeList)}]`), - expectedNodes, - ) }) diff --git a/src/utils/__tests__/ss.test.ts b/src/utils/__tests__/ss.test.ts index f583c9ea4..3ea407a0a 100644 --- a/src/utils/__tests__/ss.test.ts +++ b/src/utils/__tests__/ss.test.ts @@ -1,7 +1,7 @@ import test from 'ava' import { NodeTypeEnum } from '../../types' -import { parseSSUri } from '../ss' +import { parseSSUri, stringifySip003Options } from '../ss' test('parseSSUri', (t) => { t.deepEqual( @@ -20,3 +20,17 @@ test('parseSSUri', (t) => { }, ) }) + +test('stringifySip003Options', (t) => { + t.is( + stringifySip003Options({ + a: 123, + host: 'https://a.com/foo?bar=baz&q\\q=1&w;w=2', + mode: 'quic', + tls: true, + }), + 'a=123;host=https://a.com/foo?bar\\=baz&q\\\\q\\=1&w\\;w\\=2;mode=quic;tls=true', + ) + t.is(stringifySip003Options({}), '') + t.is(stringifySip003Options(), '') +}) diff --git a/src/utils/singbox.ts b/src/utils/singbox.ts index 0adb308e5..346b5d412 100644 --- a/src/utils/singbox.ts +++ b/src/utils/singbox.ts @@ -1,4 +1,5 @@ import { createLogger } from '@surgio/logger' + import { ERR_INVALID_FILTER } from '../constant' import { NodeFilterType, @@ -12,7 +13,7 @@ import { getHostnameFromHost, getPortFromHost, pickAndFormatKeys, -} from './index' +} from './' import { MultiplexValidator, TlsNodeConfigValidator } from '../validators' import { stringifySip003Options } from './ss' @@ -27,33 +28,16 @@ export const getSingboxNodes = function ( .filter((item): item is Record => checkNotNullish(item)) } -export const getSingboxNodesString = function ( - list: ReadonlyArray, - filter?: NodeFilterType | SortedNodeFilterType, -) { - return JSON.stringify(getSingboxNodes(list, filter)).slice(1, -1) -} - export const getSingboxNodeNames = function ( list: ReadonlyArray, filter?: NodeFilterType | SortedNodeFilterType, - prependNodeNames?: ReadonlyArray, - defaultNodeNames?: ReadonlyArray, ): ReadonlyArray { // istanbul ignore next if (arguments.length === 2 && typeof filter === 'undefined') { throw new Error(ERR_INVALID_FILTER) } - let result: string[] = [] - if (prependNodeNames) { - result = result.concat(prependNodeNames) - } - result = result.concat(getSingboxNodes(list, filter).map((item) => item.tag)) - if (result.length === 0 && defaultNodeNames) { - result = result.concat(defaultNodeNames) - } - return result + return getSingboxNodes(list, filter).map((item) => item.tag) } const typeMap = { @@ -67,7 +51,7 @@ const typeMap = { [NodeTypeEnum.Tuic]: 'tuic', [NodeTypeEnum.Wireguard]: 'wireguard', [NodeTypeEnum.Hysteria2]: 'hysteria2', -} +} as const /** * @see https://sing-box.sagernet.org/configuration/outbound/ diff --git a/src/utils/ss.ts b/src/utils/ss.ts index a5d09db77..e82bbc540 100644 --- a/src/utils/ss.ts +++ b/src/utils/ss.ts @@ -50,9 +50,7 @@ export const parseSSUri = (str: string): ShadowsocksNodeConfig => { // Marshal SIP003 plugin options in PossibleNodeConfigType to formatted string. // An example is 'a=123;host=https://a.com/foo?bar\=baz&q\\q\=1&w\;w\=2;mode=quic;tls=true', // where semicolons, equal signs and backslashes MUST be escaped with a backslash. -export const stringifySip003Options = (args: { - [key: string]: any -}): string => { +export const stringifySip003Options = (args?: Record): string => { if (!args) { return '' } diff --git a/src/validators/artifact.ts b/src/validators/artifact.ts index c43901aa7..9974a363d 100644 --- a/src/validators/artifact.ts +++ b/src/validators/artifact.ts @@ -3,6 +3,14 @@ import { z } from 'zod' export const ArtifactValidator = z.object({ name: z.string(), template: z.string(), + templateType: z + .union([z.literal('default'), z.literal('json')]) + .default('default'), + extendTemplate: z + .function() + .args(z.unknown()) + .returns(z.unknown()) + .optional(), provider: z.string(), categories: z.array(z.string()).optional(), combineProviders: z.array(z.string()).optional(), diff --git a/test/cli.cli-test.ts b/test/cli.cli-test.ts index bc3b0b2f0..16cf94646 100644 --- a/test/cli.cli-test.ts +++ b/test/cli.cli-test.ts @@ -51,6 +51,12 @@ describe('generate command', () => { const confString5 = fs.readFileSync(resolve('plain/dist/v2rayn.conf'), { encoding: 'utf8', }) + const singboxConfString = fs.readFileSync( + resolve('plain/dist/singbox.json'), + { + encoding: 'utf8', + }, + ) const conf = ini.decode(confString1) expect(fs.existsSync(resolve('plain/dist/new_path.conf'))).to.be.true @@ -59,6 +65,7 @@ describe('generate command', () => { expect(fs.existsSync(resolve('plain/dist/v2rayn.conf'))).to.be.true expect(fs.existsSync(resolve('plain/dist/custom.conf'))).to.be.true expect(fs.existsSync(resolve('plain/dist/ssd.conf'))).to.be.true + expect(fs.existsSync(resolve('plain/dist/singbox.json'))).to.be.true expect(confString1.split('\n')[0]).to.equal( '#!MANAGED-CONFIG https://example.com/ss_json.conf?access_token=abcd interval=43200 strict=false', ) @@ -66,6 +73,7 @@ describe('generate command', () => { expect(Object.keys(conf.Proxy).length).to.be.equal(4) ;(expect(confString3).to as any).matchSnapshot() ;(expect(confString5).to as any).matchSnapshot() + ;(expect(singboxConfString).to as any).matchSnapshot() }) }) diff --git a/test/cli.cli-test.ts.snap b/test/cli.cli-test.ts.snap index 0f69ebe25..18531083a 100644 --- a/test/cli.cli-test.ts.snap +++ b/test/cli.cli-test.ts.snap @@ -228,6 +228,207 @@ DOMAIN,example.com,Proxy " `; +exports[`generate command default runs generate cmd 3`] = ` +"{ + \\"dns\\": { + \\"servers\\": [ + { + \\"tag\\": \\"dnspod-doh\\", + \\"address\\": \\"https://1.12.12.12/dns-query\\", + \\"detour\\": \\"direct\\" + }, + { + \\"tag\\": \\"remote\\", + \\"address\\": \\"fakeip\\" + }, + { + \\"tag\\": \\"block\\", + \\"address\\": \\"rcode://success\\" + } + ], + \\"rules\\": [ + { + \\"rule_set\\": \\"geosite-category-ads-all\\", + \\"server\\": \\"block\\" + }, + { + \\"outbound\\": \\"any\\", + \\"server\\": \\"dnspod-doh\\" + }, + { + \\"query_type\\": [ + \\"A\\", + \\"AAAA\\" + ], + \\"server\\": \\"remote\\" + } + ], + \\"fakeip\\": { + \\"enabled\\": true, + \\"inet4_range\\": \\"198.18.0.0/15\\", + \\"inet6_range\\": \\"fc00::/18\\" + }, + \\"independent_cache\\": true + }, + \\"inbounds\\": [ + { + \\"type\\": \\"tun\\", + \\"inet4_address\\": \\"172.19.0.1/30\\", + \\"inet6_address\\": \\"fdfe:dcba:9876::1/126\\", + \\"auto_route\\": true, + \\"strict_route\\": true, + \\"sniff\\": true + } + ], + \\"outbounds\\": [ + { + \\"type\\": \\"block\\", + \\"tag\\": \\"block\\" + }, + { + \\"type\\": \\"dns\\", + \\"tag\\": \\"dns\\" + }, + { + \\"type\\": \\"direct\\", + \\"tag\\": \\"direct\\", + \\"tcp_fast_open\\": false, + \\"tcp_multi_path\\": true + }, + { + \\"type\\": \\"selector\\", + \\"tag\\": \\"proxy\\", + \\"outbounds\\": [ + \\"auto\\", + \\"🇺🇸US 1\\", + \\"🇺🇸US 2\\", + \\"🇺🇸US 3\\" + ], + \\"interrupt_exist_connections\\": false + }, + { + \\"type\\": \\"shadowsocks\\", + \\"tag\\": \\"🇺🇸US 1\\", + \\"server\\": \\"us.example.com\\", + \\"server_port\\": 443, + \\"method\\": \\"chacha20-ietf-poly1305\\", + \\"password\\": \\"password\\", + \\"plugin\\": \\"obfs-local\\", + \\"plugin_opts\\": \\"obfs=tls;obfs-host=gateway-carry.icloud.com\\", + \\"tcp_fast_open\\": true, + \\"tcp_multi_path\\": true + }, + { + \\"type\\": \\"shadowsocks\\", + \\"tag\\": \\"🇺🇸US 2\\", + \\"server\\": \\"us.example.com\\", + \\"server_port\\": 443, + \\"method\\": \\"chacha20-ietf-poly1305\\", + \\"password\\": \\"password\\", + \\"tcp_fast_open\\": true, + \\"tcp_multi_path\\": true + }, + { + \\"type\\": \\"shadowsocks\\", + \\"tag\\": \\"🇺🇸US 3\\", + \\"server\\": \\"us.example.com\\", + \\"server_port\\": 443, + \\"method\\": \\"chacha20-ietf-poly1305\\", + \\"password\\": \\"password\\", + \\"plugin\\": \\"v2ray-plugin\\", + \\"plugin_opts\\": \\"host=gateway-carry.icloud.com;mode=websocket;tls=true\\", + \\"tcp_fast_open\\": true, + \\"tcp_multi_path\\": true + } + ], + \\"route\\": { + \\"rules\\": [ + { + \\"type\\": \\"logical\\", + \\"mode\\": \\"or\\", + \\"rules\\": [ + { + \\"protocol\\": \\"dns\\" + }, + { + \\"port\\": 53 + } + ], + \\"outbound\\": \\"dns\\" + }, + { + \\"ip_is_private\\": true, + \\"outbound\\": \\"direct\\" + }, + { + \\"clash_mode\\": \\"Direct\\", + \\"outbound\\": \\"direct\\" + }, + { + \\"clash_mode\\": \\"Global\\", + \\"outbound\\": \\"proxy\\" + }, + { + \\"geosite\\": \\"geosite-category-ads-all\\", + \\"outbound\\": \\"block\\" + }, + { + \\"type\\": \\"logical\\", + \\"mode\\": \\"and\\", + \\"rules\\": [ + { + \\"rule_set\\": \\"geosite-geolocation-!cn\\", + \\"invert\\": true + }, + { + \\"rule_set\\": [ + \\"geoip-cn\\", + \\"geosite-cn\\" + ] + } + ], + \\"outbound\\": \\"direct\\" + } + ], + \\"rule_set\\": [ + { + \\"tag\\": \\"geosite-category-ads-all\\", + \\"type\\": \\"remote\\", + \\"format\\": \\"binary\\", + \\"url\\": \\"https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-category-ads-all.srs\\" + }, + { + \\"type\\": \\"remote\\", + \\"tag\\": \\"geosite-geolocation-!cn\\", + \\"format\\": \\"binary\\", + \\"url\\": \\"https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-geolocation-!cn.srs\\" + }, + { + \\"tag\\": \\"geosite-cn\\", + \\"type\\": \\"remote\\", + \\"format\\": \\"binary\\", + \\"url\\": \\"https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-cn.srs\\" + }, + { + \\"tag\\": \\"geoip-cn\\", + \\"type\\": \\"remote\\", + \\"format\\": \\"binary\\", + \\"url\\": \\"https://cdn.jsdelivr.net/gh/SagerNet/sing-geoip@rule-set/geoip-cn.srs\\" + } + ], + \\"auto_detect_interface\\": true + }, + \\"experimental\\": { + \\"cache_file\\": { + \\"enabled\\": true + }, + \\"clash_api\\": { + \\"external_controller\\": \\"127.0.0.1:9090\\" + } + } +}" +`; + exports[`generate command v2ray tls options runs generate cmd 1`] = ` "getSurgeNodes 🇺🇸US 1 = ss, us.example.com, 443, encrypt-method=chacha20-ietf-poly1305, password=password, udp-relay=false, obfs=tls, obfs-host=gateway-carry.icloud.com, tls13=false, skip-cert-verify=false diff --git a/test/fixture/plain/surgio.conf.js b/test/fixture/plain/surgio.conf.js index 25168d890..6f80025cc 100644 --- a/test/fixture/plain/surgio.conf.js +++ b/test/fixture/plain/surgio.conf.js @@ -1,8 +1,9 @@ 'use strict' const path = require('path') +const { extendOutbounds, defineSurgioConfig } = require('../../../') -module.exports = { +module.exports = defineSurgioConfig({ artifacts: [ { name: 'new_path.conf', @@ -69,6 +70,29 @@ module.exports = { }, }, }, + { + name: 'singbox.json', + template: 'singbox', + templateType: 'json', + extendTemplate: extendOutbounds( + ({ getSingboxNodes, getSingboxNodeNames, nodeList }) => [ + { + type: 'direct', + tag: 'direct', + tcp_fast_open: false, + tcp_multi_path: true, + }, + { + type: 'selector', + tag: 'proxy', + outbounds: ['auto', ...getSingboxNodeNames(nodeList)], + interrupt_exist_connections: false, + }, + ...getSingboxNodes(nodeList), + ], + ), + provider: 'ss', + }, ], urlBase: 'https://example.com/', binPath: { @@ -95,4 +119,4 @@ module.exports = { }, proxyTestUrl: 'http://www.google.com/generate_204', proxyTestInterval: 2400, -} +}) diff --git a/test/fixture/plain/template/singbox.json b/test/fixture/plain/template/singbox.json new file mode 100644 index 000000000..a84b79c82 --- /dev/null +++ b/test/fixture/plain/template/singbox.json @@ -0,0 +1,147 @@ +{ + "dns": { + "servers": [ + { + "tag": "dnspod-doh", + "address": "https://1.12.12.12/dns-query", + "detour": "direct" + }, + { + "tag": "remote", + "address": "fakeip" + }, + { + "tag": "block", + "address": "rcode://success" + } + ], + "rules": [ + { + "rule_set": "geosite-category-ads-all", + "server": "block" + }, + { + "outbound": "any", + "server": "dnspod-doh" + }, + { + "query_type": [ + "A", + "AAAA" + ], + "server": "remote" + } + ], + "fakeip": { + "enabled": true, + "inet4_range": "198.18.0.0/15", + "inet6_range": "fc00::/18" + }, + "independent_cache": true + }, + "inbounds": [ + { + "type": "tun", + "inet4_address": "172.19.0.1/30", + "inet6_address": "fdfe:dcba:9876::1/126", + "auto_route": true, + "strict_route": true, + "sniff": true + } + ], + "outbounds": [ + { + "type": "block", + "tag": "block" + }, + { + "type": "dns", + "tag": "dns" + } + ], + "route": { + "rules": [ + { + "type": "logical", + "mode": "or", + "rules": [ + { + "protocol": "dns" + }, + { + "port": 53 + } + ], + "outbound": "dns" + }, + { + "ip_is_private": true, + "outbound": "direct" + }, + { + "clash_mode": "Direct", + "outbound": "direct" + }, + { + "clash_mode": "Global", + "outbound": "proxy" + }, + { + "geosite": "geosite-category-ads-all", + "outbound": "block" + }, + { + "type": "logical", + "mode": "and", + "rules": [ + { + "rule_set": "geosite-geolocation-!cn", + "invert": true + }, + { + "rule_set": [ + "geoip-cn", + "geosite-cn" + ] + } + ], + "outbound": "direct" + } + ], + "rule_set": [ + { + "tag": "geosite-category-ads-all", + "type": "remote", + "format": "binary", + "url": "https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-category-ads-all.srs" + }, + { + "type": "remote", + "tag": "geosite-geolocation-!cn", + "format": "binary", + "url": "https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-geolocation-!cn.srs" + }, + { + "tag": "geosite-cn", + "type": "remote", + "format": "binary", + "url": "https://cdn.jsdelivr.net/gh/SagerNet/sing-geosite@rule-set/geosite-cn.srs" + }, + { + "tag": "geoip-cn", + "type": "remote", + "format": "binary", + "url": "https://cdn.jsdelivr.net/gh/SagerNet/sing-geoip@rule-set/geoip-cn.srs" + } + ], + "auto_detect_interface": true + }, + "experimental": { + "cache_file": { + "enabled": true + }, + "clash_api": { + "external_controller": "127.0.0.1:9090" + } + } +} diff --git a/tsconfig.json b/tsconfig.json index 84b848afc..c11738503 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,8 @@ { + "ts-node": { + "transpileOnly": true, + "files": true, + }, "compilerOptions": { // https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping "target": "ES2022", @@ -12,16 +16,8 @@ "skipLibCheck": true, "noEmit": true, - "strict": true /* Enable all strict type-checking options. */, /* Strict Type-Checking Options */ - // "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, - // "strictNullChecks": true /* Enable strict null checks. */, - // "strictFunctionTypes": true /* Enable strict checking of function types. */, - // "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, - // "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, - // "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, - - /* Additional Checks */ + "strict": true /* Enable all strict type-checking options. */, "noUnusedLocals": false /* Report errors on unused locals. */, "noUnusedParameters": true /* Report errors on unused parameters. */, "noImplicitReturns": true /* Report error when not all code paths in function return a value. */,