@subconverter/core 0.2.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -25,6 +25,8 @@ __export(index_exports, {
25
25
  ProxyGroupType: () => ProxyGroupType,
26
26
  ProxyType: () => ProxyType,
27
27
  QUANX_RULE_TYPES: () => QUANX_RULE_TYPES,
28
+ REGEX_BLACKLIST: () => REGEX_BLACKLIST,
29
+ RULESET_TYPE_PREFIXES: () => RULESET_TYPE_PREFIXES,
28
30
  RulesetType: () => RulesetType,
29
31
  SING_BOX_RULE_TYPES: () => SING_BOX_RULE_TYPES,
30
32
  SSR_CIPHERS: () => SSR_CIPHERS,
@@ -56,12 +58,14 @@ __export(index_exports, {
56
58
  explodeClash: () => explodeClash,
57
59
  explodeHttp: () => explodeHttp,
58
60
  explodeHttpSub: () => explodeHttpSub,
61
+ explodeHysteria: () => explodeHysteria,
59
62
  explodeHysteria2: () => explodeHysteria2,
60
63
  explodeKitsunebi: () => explodeKitsunebi,
61
64
  explodeNetch: () => explodeNetch,
62
65
  explodeNetchConf: () => explodeNetchConf,
63
66
  explodeQuan: () => explodeQuan,
64
67
  explodeShadowrocket: () => explodeShadowrocket,
68
+ explodeSnell: () => explodeSnell,
65
69
  explodeSocks: () => explodeSocks,
66
70
  explodeSs: () => explodeSs,
67
71
  explodeSsAndroid: () => explodeSsAndroid,
@@ -78,7 +82,9 @@ __export(index_exports, {
78
82
  explodeVless: () => explodeVless,
79
83
  explodeVmess: () => explodeVmess,
80
84
  explodeVmessConf: () => explodeVmessConf,
85
+ explodeWireguard: () => explodeWireguard,
81
86
  filterNodes: () => filterNodes,
87
+ filterNodesByCountry: () => filterNodesByCountry,
82
88
  findSection: () => findSection,
83
89
  fnv1a64: () => fnv1a64,
84
90
  fnv1a64Hex: () => fnv1a64Hex,
@@ -100,6 +106,7 @@ __export(index_exports, {
100
106
  hysteria2Construct: () => hysteria2Construct,
101
107
  hysteriaConstruct: () => hysteriaConstruct,
102
108
  inferUserinfoProviderFromUrl: () => inferUserinfoProviderFromUrl,
109
+ isBlacklistedPattern: () => isBlacklistedPattern,
103
110
  isBlank: () => isBlank,
104
111
  isFlattenableSsr: () => isFlattenableSsr,
105
112
  isIp: () => isIp,
@@ -113,9 +120,11 @@ __export(index_exports, {
113
120
  nodeNameRegex: () => nodeNameRegex,
114
121
  nodeNameRemove: () => nodeNameRemove,
115
122
  nodeRename: () => nodeRename,
123
+ normalizeCountryCode: () => normalizeCountryCode,
116
124
  normalizeQueryKey: () => normalizeQueryKey,
117
125
  normalizeUserinfoProvider: () => normalizeUserinfoProvider,
118
126
  pad2: () => pad2,
127
+ parseClashFlavor: () => parseClashFlavor,
119
128
  parseClashText: () => parseClashText,
120
129
  parseConfContent: () => parseConfContent,
121
130
  parseCustomProxyGroup: () => parseCustomProxyGroup,
@@ -130,6 +139,7 @@ __export(index_exports, {
130
139
  parseRegexMatch: () => parseRegexMatch,
131
140
  parseRequestSettings: () => parseRequestSettings,
132
141
  parseRulesetConfig: () => parseRulesetConfig,
142
+ parseRulesetSource: () => parseRulesetSource,
133
143
  parseSubscription: () => parseSubscription,
134
144
  parseSubscriptionUserinfo: () => parseSubscriptionUserinfo,
135
145
  parseTriBool: () => parseTriBool,
@@ -142,8 +152,6 @@ __export(index_exports, {
142
152
  queryBoolFlag: () => queryBoolFlag,
143
153
  regMatch: () => regMatch,
144
154
  regReplace: () => regReplace,
145
- regionDisplayName: () => regionDisplayName,
146
- regionFlag: () => regionFlag,
147
155
  removeEmoji: () => removeEmoji,
148
156
  renderRawIni: () => renderRawIni,
149
157
  renderTemplate: () => renderTemplate,
@@ -420,6 +428,16 @@ function createRulesetConfig() {
420
428
  }
421
429
 
422
430
  // src/types/settings.ts
431
+ function parseClashFlavor(value) {
432
+ const normalized = (value ?? "").trim().toLowerCase();
433
+ if (normalized === "premium" || normalized === "original" || normalized === "classic" || normalized === "clash") {
434
+ return "premium";
435
+ }
436
+ if (normalized === "stash") {
437
+ return "stash";
438
+ }
439
+ return "mihomo";
440
+ }
423
441
  var TargetFormat = /* @__PURE__ */ ((TargetFormat2) => {
424
442
  TargetFormat2["Clash"] = "clash";
425
443
  TargetFormat2["ClashR"] = "clashr";
@@ -448,16 +466,21 @@ function createExtraSettings() {
448
466
  authorized: false,
449
467
  autoCountry: false,
450
468
  clashClassicalRuleset: false,
469
+ clashFlavor: "mihomo",
451
470
  clashNewFieldName: true,
452
471
  clashProxiesStyle: "flow",
453
472
  clashProxyGroupsStyle: "flow",
454
473
  clashScript: false,
455
- countryLocale: "zh-CN",
456
474
  countryResolver: void 0,
457
475
  dedupe: false,
458
476
  emojiArray: [],
459
477
  enableRuleGenerator: true,
478
+ excludeCountries: [],
479
+ excludeRemarks: [],
480
+ expandRulesets: true,
460
481
  filterDeprecated: false,
482
+ includeCountries: [],
483
+ includeRemarks: [],
461
484
  managedConfigPrefix: "",
462
485
  nameRegexArray: [],
463
486
  nameRemove: [],
@@ -470,6 +493,7 @@ function createExtraSettings() {
470
493
  sortFlag: false,
471
494
  sortScript: "",
472
495
  surgeSsrPath: "",
496
+ templateVars: {},
473
497
  tfo: void 0,
474
498
  tls13: void 0,
475
499
  udp: void 0
@@ -693,7 +717,7 @@ function toBool(value) {
693
717
  return false;
694
718
  }
695
719
  const normalized = value.trim().toLowerCase();
696
- return normalized === "true" || normalized === "1";
720
+ return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on";
697
721
  }
698
722
  function strFind(haystack, needle) {
699
723
  return haystack.includes(needle);
@@ -842,6 +866,23 @@ function templateVariableNames(content) {
842
866
  }
843
867
 
844
868
  // src/settings/config.ts
869
+ var RULESET_TYPE_PREFIXES = [
870
+ ["surge:", "surge" /* Surge */],
871
+ ["quanx:", "quanx" /* QuantumultX */],
872
+ ["quantumultx:", "quanx" /* QuantumultX */],
873
+ ["clash-domain:", "clash-domain" /* ClashDomain */],
874
+ ["clash-ipcidr:", "clash-ipcidr" /* ClashIpCidr */],
875
+ ["clash-classic:", "clash-classical" /* ClashClassical */],
876
+ ["clash:", "clash-classical" /* ClashClassical */]
877
+ ];
878
+ function parseRulesetSource(source) {
879
+ for (const [prefix, type] of RULESET_TYPE_PREFIXES) {
880
+ if (source.toLowerCase().startsWith(prefix)) {
881
+ return { type, url: source.slice(prefix.length) };
882
+ }
883
+ }
884
+ return { url: source };
885
+ }
845
886
  var GROUP_TYPE_MAP = {
846
887
  fallback: "fallback" /* Fallback */,
847
888
  "load-balance": "load-balance" /* LoadBalance */,
@@ -943,8 +984,10 @@ function parseExternalConfig(content) {
943
984
  baseFiles: {},
944
985
  emoji: [],
945
986
  enableRuleGenerator: true,
987
+ excludeCountries: [],
946
988
  excludeRemarks: [],
947
989
  groups: [],
990
+ includeCountries: [],
948
991
  includeRemarks: [],
949
992
  nameRegex: [],
950
993
  nameRemove: [],
@@ -1013,6 +1056,22 @@ function parseExternalConfig(content) {
1013
1056
  result.includeRemarks.push(value);
1014
1057
  }
1015
1058
  break;
1059
+ case "include_country":
1060
+ for (const fragment of value.split(",")) {
1061
+ const trimmed = fragment.trim().toUpperCase();
1062
+ if (trimmed !== "") {
1063
+ result.includeCountries.push(trimmed);
1064
+ }
1065
+ }
1066
+ break;
1067
+ case "exclude_country":
1068
+ for (const fragment of value.split(",")) {
1069
+ const trimmed = fragment.trim().toUpperCase();
1070
+ if (trimmed !== "") {
1071
+ result.excludeCountries.push(trimmed);
1072
+ }
1073
+ }
1074
+ break;
1016
1075
  case "exclude_remarks":
1017
1076
  case "exclude":
1018
1077
  if (value !== "") {
@@ -1067,6 +1126,9 @@ function listArg(query, key) {
1067
1126
  const value = getUrlArg(query, key);
1068
1127
  return value === "" ? [] : value.split(",");
1069
1128
  }
1129
+ function countryListArg(query, key) {
1130
+ return getAllUrlArgs(query, key).flatMap((raw) => raw.split(",")).map((raw) => raw.trim().toUpperCase()).filter((raw) => raw !== "");
1131
+ }
1070
1132
  function resolveNewFieldName(query) {
1071
1133
  if (boolArg(query, "legacy") || boolArg(query, "legacy_field_name") || boolArg(query, "legacy-field-name")) {
1072
1134
  return false;
@@ -1105,13 +1167,19 @@ function createRequestSettings() {
1105
1167
  appendType: false,
1106
1168
  autoCountry: false,
1107
1169
  autoExtract: false,
1170
+ clashFlavor: "mihomo",
1108
1171
  clashScript: false,
1109
1172
  classicalRuleset: false,
1110
1173
  dedupe: false,
1174
+ devId: "",
1111
1175
  emoji: [],
1176
+ excludeCountries: [],
1112
1177
  excludeRemarks: [],
1178
+ expand: true,
1179
+ filename: "",
1113
1180
  filterDeprecated: false,
1114
1181
  group: [],
1182
+ includeCountries: [],
1115
1183
  includeRemarks: [],
1116
1184
  insert: false,
1117
1185
  interval: 0,
@@ -1129,6 +1197,7 @@ function createRequestSettings() {
1129
1197
  script: "",
1130
1198
  scv: void 0,
1131
1199
  sort: false,
1200
+ strict: false,
1132
1201
  target: "auto" /* Auto */,
1133
1202
  tfo: void 0,
1134
1203
  tls13: void 0,
@@ -1157,6 +1226,11 @@ function parseRequestSettings(query) {
1157
1226
  settings.filterDeprecated = boolArg(query, "filter_deprecated");
1158
1227
  settings.newFieldName = resolveNewFieldName(query);
1159
1228
  settings.classicalRuleset = boolArg(query, "classical");
1229
+ settings.clashFlavor = parseClashFlavor(firstArg(query, "flavor"));
1230
+ settings.expand = queryBoolFlag(query, "expand") ?? true;
1231
+ settings.strict = boolArg(query, "strict");
1232
+ settings.filename = firstArg(query, "filename");
1233
+ settings.devId = firstArg(query, "dev_id", "dev-id", "device_id", "device-id");
1160
1234
  settings.insert = getUrlArg(query, "insert") !== "";
1161
1235
  settings.clashScript = boolArg(query, "clash_script");
1162
1236
  settings.autoExtract = boolArg(query, "auto_extract");
@@ -1172,6 +1246,8 @@ function parseRequestSettings(query) {
1172
1246
  settings.proxyGroupsStyle = getUrlArg(query, "proxy_groups_style") || "flow";
1173
1247
  settings.includeRemarks = listArg(query, "include");
1174
1248
  settings.excludeRemarks = listArg(query, "exclude");
1249
+ settings.includeCountries = countryListArg(query, "include_country");
1250
+ settings.excludeCountries = countryListArg(query, "exclude_country");
1175
1251
  settings.userinfoUrl = firstArg(query, "userinfo_url", "userinfo-url", "userinfo");
1176
1252
  settings.userinfoProvider = firstArg(
1177
1253
  query,
@@ -2047,485 +2123,493 @@ function explodeClash(root, nodes) {
2047
2123
  }
2048
2124
  let index = nodes.length;
2049
2125
  for (const single of list) {
2050
- const proxyType = asString(get(single, "type")).toLowerCase();
2051
- const ps = asString(get(single, "name"));
2052
- const server = asString(get(single, "server"));
2053
- const port = asString(get(single, "port"));
2054
- let underlyingProxy = asString(get(single, "underlying-proxy"));
2055
- if (underlyingProxy === "") {
2056
- underlyingProxy = asString(get(single, "dialer-proxy"));
2057
- }
2058
- if (port === "" || port === "0") {
2059
- continue;
2060
- }
2061
- const udp = asTri(get(single, "udp"));
2062
- const tfo = asTri(get(single, "fast-open"));
2063
- const scv = asTri(get(single, "skip-cert-verify"));
2064
- const node = createProxy();
2065
- let group2 = "";
2066
- switch (proxyType) {
2067
- case "vmess": {
2068
- group2 = DEFAULT_GROUPS.v2ray;
2069
- const id = asString(get(single, "uuid"));
2070
- const aid = asString(get(single, "alterId"));
2071
- const cipher = asString(get(single, "cipher"));
2072
- const net = get(single, "network") !== void 0 ? asString(get(single, "network")) : "tcp";
2073
- const sni = asString(get(single, "servername"));
2074
- let path = "";
2075
- let host = "";
2076
- let edge = "";
2077
- switch (net) {
2078
- case "http":
2079
- path = asString(get(get(get(single, "http-opts"), "path"), 0));
2080
- host = asString(get(get(get(get(single, "http-opts"), "headers"), "Host"), 0));
2081
- edge = "";
2082
- break;
2083
- case "ws":
2084
- if (get(single, "ws-opts") !== void 0) {
2085
- const wsOpts = get(single, "ws-opts");
2086
- path = get(wsOpts, "path") !== void 0 ? asString(get(wsOpts, "path")) : "/";
2087
- host = asString(get(get(wsOpts, "headers"), "Host"));
2088
- edge = asString(get(get(wsOpts, "headers"), "Edge"));
2089
- } else {
2090
- path = get(single, "ws-path") !== void 0 ? asString(get(single, "ws-path")) : "/";
2091
- host = asString(get(get(single, "ws-headers"), "Host"));
2092
- edge = asString(get(get(single, "ws-headers"), "Edge"));
2093
- }
2094
- break;
2095
- case "h2":
2096
- path = asString(get(get(single, "h2-opts"), "path"));
2097
- host = asString(get(get(get(single, "h2-opts"), "host"), 0));
2098
- edge = "";
2099
- break;
2100
- case "grpc":
2101
- host = asString(get(single, "servername"));
2102
- path = asString(get(get(single, "grpc-opts"), "grpc-service-name"));
2103
- edge = "";
2104
- break;
2105
- default:
2106
- break;
2107
- }
2108
- const tls = asBool(get(single, "tls")) ? "tls" : "";
2109
- vmessConstruct(node, {
2110
- add: server,
2111
- aid,
2112
- cipher,
2113
- edge,
2114
- group: group2,
2115
- host,
2116
- id,
2117
- net,
2118
- path,
2119
- port,
2120
- remark: ps,
2121
- scv,
2122
- sni,
2123
- tfo,
2124
- tls,
2125
- type: "",
2126
- udp,
2127
- underlyingProxy
2128
- });
2129
- break;
2126
+ try {
2127
+ if (parseClashNode(single, nodes, index)) {
2128
+ index += 1;
2130
2129
  }
2131
- case "ss": {
2132
- group2 = DEFAULT_GROUPS.ss;
2133
- let cipher = asString(get(single, "cipher"));
2134
- const password = asString(get(single, "password"));
2135
- let plugin = "";
2136
- let pluginOption = "";
2137
- let pluginMode = "";
2138
- let pluginHost = "";
2139
- let pluginMux = "";
2140
- let pluginTls = "";
2141
- let path = "";
2142
- if (get(single, "plugin") !== void 0) {
2143
- const pluginName = asString(get(single, "plugin")).toLowerCase();
2144
- const pluginOpts = get(single, "plugin-opts");
2145
- if (pluginName === "obfs") {
2146
- plugin = "obfs-local";
2147
- pluginMode = asString(get(pluginOpts, "mode"));
2148
- pluginHost = asString(get(pluginOpts, "host"));
2149
- } else if (pluginName === "v2ray-plugin") {
2150
- plugin = "v2ray-plugin";
2151
- pluginMode = asString(get(pluginOpts, "mode"));
2152
- pluginHost = asString(get(pluginOpts, "host"));
2153
- pluginTls = asBool(get(pluginOpts, "tls")) ? "tls;" : "";
2154
- path = asString(get(pluginOpts, "path"));
2155
- pluginMux = asBool(get(pluginOpts, "mux")) ? "mux=4;" : "";
2156
- }
2157
- } else if (get(single, "obfs") !== void 0) {
2158
- plugin = "obfs-local";
2159
- pluginMode = asString(get(single, "obfs"));
2160
- pluginHost = asString(get(single, "obfs-host"));
2161
- }
2162
- if (plugin === "simple-obfs" || plugin === "obfs-local") {
2163
- pluginOption = `obfs=${pluginMode}`;
2164
- pluginOption += pluginHost === "" ? "" : `;obfs-host=${pluginHost}`;
2165
- } else if (plugin === "v2ray-plugin") {
2166
- pluginOption = `mode=${pluginMode};${pluginTls}${pluginMux}`;
2167
- if (pluginHost !== "") {
2168
- pluginOption += `host=${pluginHost};`;
2169
- }
2170
- if (path !== "") {
2171
- pluginOption += `path=${path};`;
2172
- }
2173
- if (pluginMux !== "") {
2174
- pluginOption += `mux=${pluginMux};`;
2130
+ } catch {
2131
+ }
2132
+ }
2133
+ }
2134
+ function parseClashNode(single, nodes, index) {
2135
+ const proxyType = asString(get(single, "type")).toLowerCase();
2136
+ const ps = asString(get(single, "name"));
2137
+ const server = asString(get(single, "server"));
2138
+ const port = asString(get(single, "port"));
2139
+ let underlyingProxy = asString(get(single, "underlying-proxy"));
2140
+ if (underlyingProxy === "") {
2141
+ underlyingProxy = asString(get(single, "dialer-proxy"));
2142
+ }
2143
+ if (port === "" || port === "0") {
2144
+ return false;
2145
+ }
2146
+ const udp = asTri(get(single, "udp"));
2147
+ const tfo = asTri(get(single, "fast-open"));
2148
+ const scv = asTri(get(single, "skip-cert-verify"));
2149
+ const node = createProxy();
2150
+ let group2 = "";
2151
+ switch (proxyType) {
2152
+ case "vmess": {
2153
+ group2 = DEFAULT_GROUPS.v2ray;
2154
+ const id = asString(get(single, "uuid"));
2155
+ const aid = asString(get(single, "alterId"));
2156
+ const cipher = asString(get(single, "cipher"));
2157
+ const net = get(single, "network") !== void 0 ? asString(get(single, "network")) : "tcp";
2158
+ const sni = asString(get(single, "servername"));
2159
+ let path = "";
2160
+ let host = "";
2161
+ let edge = "";
2162
+ switch (net) {
2163
+ case "http":
2164
+ path = asString(get(get(get(single, "http-opts"), "path"), 0));
2165
+ host = asString(get(get(get(get(single, "http-opts"), "headers"), "Host"), 0));
2166
+ edge = "";
2167
+ break;
2168
+ case "ws":
2169
+ if (get(single, "ws-opts") !== void 0) {
2170
+ const wsOpts = get(single, "ws-opts");
2171
+ path = get(wsOpts, "path") !== void 0 ? asString(get(wsOpts, "path")) : "/";
2172
+ host = asString(get(get(wsOpts, "headers"), "Host"));
2173
+ edge = asString(get(get(wsOpts, "headers"), "Edge"));
2174
+ } else {
2175
+ path = get(single, "ws-path") !== void 0 ? asString(get(single, "ws-path")) : "/";
2176
+ host = asString(get(get(single, "ws-headers"), "Host"));
2177
+ edge = asString(get(get(single, "ws-headers"), "Edge"));
2175
2178
  }
2176
- }
2177
- if (cipher === "AEAD_CHACHA20_POLY1305") {
2178
- cipher = "chacha20-ietf-poly1305";
2179
- } else if (cipher.includes("AEAD")) {
2180
- cipher = cipher.replace(/AEAD_/g, "").replace(/_/g, "-").toLowerCase();
2181
- }
2182
- ssConstruct(node, {
2183
- group: group2,
2184
- method: cipher,
2185
- password,
2186
- plugin,
2187
- pluginOption,
2188
- port,
2189
- remark: ps,
2190
- scv,
2191
- server,
2192
- tfo,
2193
- udp,
2194
- underlyingProxy
2195
- });
2196
- break;
2197
- }
2198
- case "socks5": {
2199
- group2 = DEFAULT_GROUPS.socks;
2200
- const user = asString(get(single, "username"));
2201
- const password = asString(get(single, "password"));
2202
- socksConstruct(node, {
2203
- group: group2,
2204
- password,
2205
- port,
2206
- remark: ps,
2207
- server,
2208
- underlyingProxy,
2209
- username: user
2210
- });
2211
- break;
2212
- }
2213
- case "ssr": {
2214
- group2 = DEFAULT_GROUPS.ssr;
2215
- let cipher = asString(get(single, "cipher"));
2216
- if (cipher === "dummy") {
2217
- cipher = "none";
2218
- }
2219
- const password = asString(get(single, "password"));
2220
- const protocol = asString(get(single, "protocol"));
2221
- const obfs = asString(get(single, "obfs"));
2222
- const protocolParam = get(single, "protocol-param") !== void 0 ? asString(get(single, "protocol-param")) : asString(get(single, "protocolparam"));
2223
- const obfsParam = get(single, "obfs-param") !== void 0 ? asString(get(single, "obfs-param")) : asString(get(single, "obfsparam"));
2224
- ssrConstruct(node, {
2225
- group: group2,
2226
- method: cipher,
2227
- obfs,
2228
- obfsParam,
2229
- password,
2230
- port,
2231
- protocol,
2232
- protocolParam,
2233
- remark: ps,
2234
- scv,
2235
- server,
2236
- tfo,
2237
- udp,
2238
- underlyingProxy
2239
- });
2240
- break;
2241
- }
2242
- case "http": {
2243
- group2 = DEFAULT_GROUPS.http;
2244
- const user = asString(get(single, "username"));
2245
- const password = asString(get(single, "password"));
2246
- const tls = asString(get(single, "tls"));
2247
- httpConstruct(node, {
2248
- group: group2,
2249
- password,
2250
- port,
2251
- remark: ps,
2252
- scv,
2253
- server,
2254
- tfo,
2255
- tls: tls === "true",
2256
- underlyingProxy,
2257
- username: user
2258
- });
2259
- break;
2179
+ break;
2180
+ case "h2":
2181
+ path = asString(get(get(single, "h2-opts"), "path"));
2182
+ host = asString(get(get(get(single, "h2-opts"), "host"), 0));
2183
+ edge = "";
2184
+ break;
2185
+ case "grpc":
2186
+ host = asString(get(single, "servername"));
2187
+ path = asString(get(get(single, "grpc-opts"), "grpc-service-name"));
2188
+ edge = "";
2189
+ break;
2190
+ default:
2191
+ break;
2260
2192
  }
2261
- case "trojan": {
2262
- group2 = DEFAULT_GROUPS.trojan;
2263
- const password = asString(get(single, "password"));
2264
- const host = asString(get(single, "sni"));
2265
- let net = asString(get(single, "network"));
2266
- let path = "";
2267
- switch (net) {
2268
- case "grpc":
2269
- path = asString(get(get(single, "grpc-opts"), "grpc-service-name"));
2270
- break;
2271
- case "ws":
2272
- path = asString(get(get(single, "ws-opts"), "path"));
2273
- break;
2274
- default:
2275
- net = "tcp";
2276
- path = "";
2277
- break;
2193
+ const tls = asBool(get(single, "tls")) ? "tls" : "";
2194
+ vmessConstruct(node, {
2195
+ add: server,
2196
+ aid,
2197
+ cipher,
2198
+ edge,
2199
+ group: group2,
2200
+ host,
2201
+ id,
2202
+ net,
2203
+ path,
2204
+ port,
2205
+ remark: ps,
2206
+ scv,
2207
+ sni,
2208
+ tfo,
2209
+ tls,
2210
+ type: "",
2211
+ udp,
2212
+ underlyingProxy
2213
+ });
2214
+ break;
2215
+ }
2216
+ case "ss": {
2217
+ group2 = DEFAULT_GROUPS.ss;
2218
+ let cipher = asString(get(single, "cipher"));
2219
+ const password = asString(get(single, "password"));
2220
+ let plugin = "";
2221
+ let pluginOption = "";
2222
+ let pluginMode = "";
2223
+ let pluginHost = "";
2224
+ let pluginMux = "";
2225
+ let pluginTls = "";
2226
+ let path = "";
2227
+ if (get(single, "plugin") !== void 0) {
2228
+ const pluginName = asString(get(single, "plugin")).toLowerCase();
2229
+ const pluginOpts = get(single, "plugin-opts");
2230
+ if (pluginName === "obfs") {
2231
+ plugin = "obfs-local";
2232
+ pluginMode = asString(get(pluginOpts, "mode"));
2233
+ pluginHost = asString(get(pluginOpts, "host"));
2234
+ } else if (pluginName === "v2ray-plugin") {
2235
+ plugin = "v2ray-plugin";
2236
+ pluginMode = asString(get(pluginOpts, "mode"));
2237
+ pluginHost = asString(get(pluginOpts, "host"));
2238
+ pluginTls = asBool(get(pluginOpts, "tls")) ? "tls;" : "";
2239
+ path = asString(get(pluginOpts, "path"));
2240
+ pluginMux = asBool(get(pluginOpts, "mux")) ? "mux=4;" : "";
2278
2241
  }
2279
- trojanConstruct(node, {
2280
- group: group2,
2281
- host,
2282
- network: net,
2283
- password,
2284
- path,
2285
- port,
2286
- remark: ps,
2287
- scv,
2288
- server,
2289
- tfo,
2290
- tlsSecure: true,
2291
- udp,
2292
- underlyingProxy
2293
- });
2294
- break;
2295
- }
2296
- case "snell": {
2297
- group2 = DEFAULT_GROUPS.snell;
2298
- const password = asString(get(single, "psk"));
2299
- const obfs = asString(get(get(single, "obfs-opts"), "mode"));
2300
- const host = asString(get(get(single, "obfs-opts"), "host"));
2301
- const version = asString(get(single, "version"));
2302
- snellConstruct(node, {
2303
- group: group2,
2304
- host,
2305
- obfs,
2306
- password,
2307
- port,
2308
- remark: ps,
2309
- scv,
2310
- server,
2311
- tfo,
2312
- udp,
2313
- underlyingProxy,
2314
- version: toInt(version)
2315
- });
2316
- break;
2317
- }
2318
- case "wireguard": {
2319
- group2 = DEFAULT_GROUPS.wireguard;
2320
- const publicKey = asString(get(single, "public-key"));
2321
- const privateKey = asString(get(single, "private-key"));
2322
- const dns = get(single, "dns");
2323
- const dnsServers = Array.isArray(dns) ? dns.map((d) => String(d)) : dns ? [String(dns)] : [];
2324
- const mtu = asString(get(single, "mtu"));
2325
- const psk = asString(get(single, "preshared-key"));
2326
- const ip = asString(get(single, "ip"));
2327
- const ipv6 = asString(get(single, "ipv6"));
2328
- wireGuardConstruct(node, {
2329
- dnsServers,
2330
- group: group2,
2331
- keepAlive: "0",
2332
- mtu,
2333
- port,
2334
- preSharedKey: psk,
2335
- privateKey,
2336
- publicKey,
2337
- remark: ps,
2338
- selfIp: ip,
2339
- selfIpv6: ipv6,
2340
- server,
2341
- testUrl: "",
2342
- udp,
2343
- underlyingProxy
2344
- });
2345
- break;
2346
- }
2347
- case "hysteria": {
2348
- group2 = DEFAULT_GROUPS.hysteria;
2349
- const ports = asString(get(single, "ports"));
2350
- const protocol = asString(get(single, "protocol"));
2351
- const obfsProtocol = asString(get(single, "obfs-protocol"));
2352
- const up = asString(get(single, "up"));
2353
- const upSpeed = asString(get(single, "up-speed"));
2354
- const down = asString(get(single, "down"));
2355
- const downSpeed = asString(get(single, "down-speed"));
2356
- const auth = asString(get(single, "auth"));
2357
- let authStr = asString(get(single, "auth-str"));
2358
- if (authStr === "") {
2359
- authStr = asString(get(single, "auth_str"));
2242
+ } else if (get(single, "obfs") !== void 0) {
2243
+ plugin = "obfs-local";
2244
+ pluginMode = asString(get(single, "obfs"));
2245
+ pluginHost = asString(get(single, "obfs-host"));
2246
+ }
2247
+ if (plugin === "simple-obfs" || plugin === "obfs-local") {
2248
+ pluginOption = `obfs=${pluginMode}`;
2249
+ pluginOption += pluginHost === "" ? "" : `;obfs-host=${pluginHost}`;
2250
+ } else if (plugin === "v2ray-plugin") {
2251
+ pluginOption = `mode=${pluginMode};${pluginTls}${pluginMux}`;
2252
+ if (pluginHost !== "") {
2253
+ pluginOption += `host=${pluginHost};`;
2360
2254
  }
2361
- const obfs = asString(get(single, "obfs"));
2362
- const sni = asString(get(single, "sni"));
2363
- const fingerprint = asString(get(single, "fingerprint"));
2364
- const alpnValue = get(single, "alpn");
2365
- const alpn = Array.isArray(alpnValue) ? asString(alpnValue[0]) : asString(alpnValue);
2366
- const ca = asString(get(single, "ca"));
2367
- const caStr = asString(get(single, "ca-str"));
2368
- const recvWindowConn = asString(get(single, "recv-window-conn"));
2369
- const recvWindow = asString(get(single, "recv-window"));
2370
- let disableMtuDiscovery = asString(get(single, "disable-mtu-discovery"));
2371
- if (disableMtuDiscovery === "") {
2372
- disableMtuDiscovery = asString(get(single, "disable_mtu_discovery"));
2255
+ if (path !== "") {
2256
+ pluginOption += `path=${path};`;
2373
2257
  }
2374
- const hopInterval = asString(get(single, "hop-interval"));
2375
- hysteriaConstruct(node, {
2376
- alpn,
2377
- auth,
2378
- authStr,
2379
- ca,
2380
- caStr,
2381
- disableMtuDiscovery,
2382
- down,
2383
- downSpeed,
2384
- fingerprint,
2385
- group: group2,
2386
- hopInterval,
2387
- obfs,
2388
- obfsProtocol,
2389
- port,
2390
- ports,
2391
- protocol,
2392
- recvWindow,
2393
- recvWindowConn,
2394
- remark: ps,
2395
- scv,
2396
- server,
2397
- sni,
2398
- tfo,
2399
- underlyingProxy,
2400
- up,
2401
- upSpeed
2402
- });
2403
- break;
2404
- }
2405
- case "hysteria2": {
2406
- group2 = DEFAULT_GROUPS.hysteria2;
2407
- const ports = asString(get(single, "ports"));
2408
- const up = asString(get(single, "up"));
2409
- const down = asString(get(single, "down"));
2410
- let password = asString(get(single, "password"));
2411
- if (password === "") {
2412
- password = asString(get(single, "auth"));
2258
+ if (pluginMux !== "") {
2259
+ pluginOption += `mux=${pluginMux};`;
2413
2260
  }
2414
- const obfs = asString(get(single, "obfs"));
2415
- const obfsPassword = asString(get(single, "obfs-password"));
2416
- const sni = asString(get(single, "sni"));
2417
- const fingerprint = asString(get(single, "fingerprint"));
2418
- const alpnValue = get(single, "alpn");
2419
- const alpn = Array.isArray(alpnValue) ? asString(alpnValue[0]) : asString(alpnValue);
2420
- const ca = asString(get(single, "ca"));
2421
- const caStr = asString(get(single, "ca-str"));
2422
- const cwnd = asString(get(single, "cwnd"));
2423
- const hopInterval = asString(get(single, "hop-interval"));
2424
- hysteria2Construct(node, {
2425
- alpn,
2426
- ca,
2427
- caStr,
2428
- cwnd,
2429
- down,
2430
- fingerprint,
2431
- group: group2,
2432
- hopInterval,
2433
- obfs,
2434
- obfsPassword,
2435
- password,
2436
- port,
2437
- ports,
2438
- remark: ps,
2439
- scv,
2440
- server,
2441
- sni,
2442
- tfo,
2443
- underlyingProxy,
2444
- up
2445
- });
2446
- break;
2447
- }
2448
- case "anytls": {
2449
- group2 = DEFAULT_GROUPS.anytls;
2450
- const password = asString(get(single, "password"));
2451
- const sni = asString(get(single, "sni"));
2452
- anyTlsConstruct(node, {
2453
- group: group2,
2454
- password,
2455
- port,
2456
- remark: ps,
2457
- scv,
2458
- server,
2459
- sni,
2460
- tfo,
2461
- udp,
2462
- underlyingProxy
2463
- });
2464
- break;
2465
2261
  }
2466
- case "vless": {
2467
- group2 = DEFAULT_GROUPS.v2ray;
2468
- const userId = asString(get(single, "uuid"));
2469
- const net = asString(get(single, "network"));
2470
- const tls = asString(get(single, "tls"));
2471
- let path = "";
2472
- let host = "";
2473
- if (net === "ws") {
2474
- path = asString(get(get(single, "ws-opts"), "path"));
2475
- host = asString(get(get(get(single, "ws-opts"), "headers"), "Host"));
2476
- } else if (net === "grpc") {
2477
- path = asString(get(get(single, "grpc-opts"), "grpc-service-name"));
2478
- }
2479
- const alpnValue = get(single, "alpn");
2480
- vlessConstruct(node, {
2481
- alpn: Array.isArray(alpnValue) ? alpnValue.map((a) => String(a)) : void 0,
2482
- encryption: asString(get(single, "encryption")),
2483
- fingerprint: asString(get(single, "client-fingerprint")),
2484
- flow: asString(get(single, "flow")),
2485
- group: group2,
2486
- host,
2487
- network: net,
2488
- path,
2489
- port,
2490
- remark: ps,
2491
- scv,
2492
- server,
2493
- sni: asString(get(single, "servername")),
2494
- tfo,
2495
- tls: tls === "true" ? "tls" : "",
2496
- udp,
2497
- underlyingProxy,
2498
- userId
2499
- });
2500
- break;
2262
+ if (cipher === "AEAD_CHACHA20_POLY1305") {
2263
+ cipher = "chacha20-ietf-poly1305";
2264
+ } else if (cipher.includes("AEAD")) {
2265
+ cipher = cipher.replace(/AEAD_/g, "").replace(/_/g, "-").toLowerCase();
2501
2266
  }
2502
- case "tuic": {
2503
- group2 = DEFAULT_GROUPS.v2ray;
2504
- const alpnValue = get(single, "alpn");
2505
- tuicConstruct(node, {
2506
- alpn: Array.isArray(alpnValue) ? alpnValue.map((a) => String(a)) : void 0,
2507
- congestionControl: asString(get(single, "congestion-controller")),
2508
- group: group2,
2509
- password: asString(get(single, "password")),
2510
- port,
2511
- remark: ps,
2512
- scv,
2513
- server,
2514
- sni: asString(get(single, "sni")),
2515
- tfo,
2516
- udp,
2517
- underlyingProxy,
2518
- userId: asString(get(single, "uuid"))
2519
- });
2520
- break;
2267
+ ssConstruct(node, {
2268
+ group: group2,
2269
+ method: cipher,
2270
+ password,
2271
+ plugin,
2272
+ pluginOption,
2273
+ port,
2274
+ remark: ps,
2275
+ scv,
2276
+ server,
2277
+ tfo,
2278
+ udp,
2279
+ underlyingProxy
2280
+ });
2281
+ break;
2282
+ }
2283
+ case "socks5": {
2284
+ group2 = DEFAULT_GROUPS.socks;
2285
+ const user = asString(get(single, "username"));
2286
+ const password = asString(get(single, "password"));
2287
+ socksConstruct(node, {
2288
+ group: group2,
2289
+ password,
2290
+ port,
2291
+ remark: ps,
2292
+ server,
2293
+ underlyingProxy,
2294
+ username: user
2295
+ });
2296
+ break;
2297
+ }
2298
+ case "ssr": {
2299
+ group2 = DEFAULT_GROUPS.ssr;
2300
+ let cipher = asString(get(single, "cipher"));
2301
+ if (cipher === "dummy") {
2302
+ cipher = "none";
2303
+ }
2304
+ const password = asString(get(single, "password"));
2305
+ const protocol = asString(get(single, "protocol"));
2306
+ const obfs = asString(get(single, "obfs"));
2307
+ const protocolParam = get(single, "protocol-param") !== void 0 ? asString(get(single, "protocol-param")) : asString(get(single, "protocolparam"));
2308
+ const obfsParam = get(single, "obfs-param") !== void 0 ? asString(get(single, "obfs-param")) : asString(get(single, "obfsparam"));
2309
+ ssrConstruct(node, {
2310
+ group: group2,
2311
+ method: cipher,
2312
+ obfs,
2313
+ obfsParam,
2314
+ password,
2315
+ port,
2316
+ protocol,
2317
+ protocolParam,
2318
+ remark: ps,
2319
+ scv,
2320
+ server,
2321
+ tfo,
2322
+ udp,
2323
+ underlyingProxy
2324
+ });
2325
+ break;
2326
+ }
2327
+ case "http": {
2328
+ group2 = DEFAULT_GROUPS.http;
2329
+ const user = asString(get(single, "username"));
2330
+ const password = asString(get(single, "password"));
2331
+ const tls = asString(get(single, "tls"));
2332
+ httpConstruct(node, {
2333
+ group: group2,
2334
+ password,
2335
+ port,
2336
+ remark: ps,
2337
+ scv,
2338
+ server,
2339
+ tfo,
2340
+ tls: tls === "true",
2341
+ underlyingProxy,
2342
+ username: user
2343
+ });
2344
+ break;
2345
+ }
2346
+ case "trojan": {
2347
+ group2 = DEFAULT_GROUPS.trojan;
2348
+ const password = asString(get(single, "password"));
2349
+ const host = asString(get(single, "sni"));
2350
+ let net = asString(get(single, "network"));
2351
+ let path = "";
2352
+ switch (net) {
2353
+ case "grpc":
2354
+ path = asString(get(get(single, "grpc-opts"), "grpc-service-name"));
2355
+ break;
2356
+ case "ws":
2357
+ path = asString(get(get(single, "ws-opts"), "path"));
2358
+ break;
2359
+ default:
2360
+ net = "tcp";
2361
+ path = "";
2362
+ break;
2521
2363
  }
2522
- default:
2523
- continue;
2364
+ trojanConstruct(node, {
2365
+ group: group2,
2366
+ host,
2367
+ network: net,
2368
+ password,
2369
+ path,
2370
+ port,
2371
+ remark: ps,
2372
+ scv,
2373
+ server,
2374
+ tfo,
2375
+ tlsSecure: true,
2376
+ udp,
2377
+ underlyingProxy
2378
+ });
2379
+ break;
2524
2380
  }
2525
- node.id = index;
2526
- nodes.push(node);
2527
- index += 1;
2381
+ case "snell": {
2382
+ group2 = DEFAULT_GROUPS.snell;
2383
+ const password = asString(get(single, "psk"));
2384
+ const obfs = asString(get(get(single, "obfs-opts"), "mode"));
2385
+ const host = asString(get(get(single, "obfs-opts"), "host"));
2386
+ const version = asString(get(single, "version"));
2387
+ snellConstruct(node, {
2388
+ group: group2,
2389
+ host,
2390
+ obfs,
2391
+ password,
2392
+ port,
2393
+ remark: ps,
2394
+ scv,
2395
+ server,
2396
+ tfo,
2397
+ udp,
2398
+ underlyingProxy,
2399
+ version: toInt(version)
2400
+ });
2401
+ break;
2402
+ }
2403
+ case "wireguard": {
2404
+ group2 = DEFAULT_GROUPS.wireguard;
2405
+ const publicKey = asString(get(single, "public-key"));
2406
+ const privateKey = asString(get(single, "private-key"));
2407
+ const dns = get(single, "dns");
2408
+ const dnsServers = Array.isArray(dns) ? dns.map((d) => String(d)) : dns ? [String(dns)] : [];
2409
+ const mtu = asString(get(single, "mtu"));
2410
+ const psk = asString(get(single, "preshared-key"));
2411
+ const ip = asString(get(single, "ip"));
2412
+ const ipv6 = asString(get(single, "ipv6"));
2413
+ wireGuardConstruct(node, {
2414
+ dnsServers,
2415
+ group: group2,
2416
+ keepAlive: "0",
2417
+ mtu,
2418
+ port,
2419
+ preSharedKey: psk,
2420
+ privateKey,
2421
+ publicKey,
2422
+ remark: ps,
2423
+ selfIp: ip,
2424
+ selfIpv6: ipv6,
2425
+ server,
2426
+ testUrl: "",
2427
+ udp,
2428
+ underlyingProxy
2429
+ });
2430
+ break;
2431
+ }
2432
+ case "hysteria": {
2433
+ group2 = DEFAULT_GROUPS.hysteria;
2434
+ const ports = asString(get(single, "ports"));
2435
+ const protocol = asString(get(single, "protocol"));
2436
+ const obfsProtocol = asString(get(single, "obfs-protocol"));
2437
+ const up = asString(get(single, "up"));
2438
+ const upSpeed = asString(get(single, "up-speed"));
2439
+ const down = asString(get(single, "down"));
2440
+ const downSpeed = asString(get(single, "down-speed"));
2441
+ const auth = asString(get(single, "auth"));
2442
+ let authStr = asString(get(single, "auth-str"));
2443
+ if (authStr === "") {
2444
+ authStr = asString(get(single, "auth_str"));
2445
+ }
2446
+ const obfs = asString(get(single, "obfs"));
2447
+ const sni = asString(get(single, "sni"));
2448
+ const fingerprint = asString(get(single, "fingerprint"));
2449
+ const alpnValue = get(single, "alpn");
2450
+ const alpn = Array.isArray(alpnValue) ? asString(alpnValue[0]) : asString(alpnValue);
2451
+ const ca = asString(get(single, "ca"));
2452
+ const caStr = asString(get(single, "ca-str"));
2453
+ const recvWindowConn = asString(get(single, "recv-window-conn"));
2454
+ const recvWindow = asString(get(single, "recv-window"));
2455
+ let disableMtuDiscovery = asString(get(single, "disable-mtu-discovery"));
2456
+ if (disableMtuDiscovery === "") {
2457
+ disableMtuDiscovery = asString(get(single, "disable_mtu_discovery"));
2458
+ }
2459
+ const hopInterval = asString(get(single, "hop-interval"));
2460
+ hysteriaConstruct(node, {
2461
+ alpn,
2462
+ auth,
2463
+ authStr,
2464
+ ca,
2465
+ caStr,
2466
+ disableMtuDiscovery,
2467
+ down,
2468
+ downSpeed,
2469
+ fingerprint,
2470
+ group: group2,
2471
+ hopInterval,
2472
+ obfs,
2473
+ obfsProtocol,
2474
+ port,
2475
+ ports,
2476
+ protocol,
2477
+ recvWindow,
2478
+ recvWindowConn,
2479
+ remark: ps,
2480
+ scv,
2481
+ server,
2482
+ sni,
2483
+ tfo,
2484
+ underlyingProxy,
2485
+ up,
2486
+ upSpeed
2487
+ });
2488
+ break;
2489
+ }
2490
+ case "hysteria2": {
2491
+ group2 = DEFAULT_GROUPS.hysteria2;
2492
+ const ports = asString(get(single, "ports"));
2493
+ const up = asString(get(single, "up"));
2494
+ const down = asString(get(single, "down"));
2495
+ let password = asString(get(single, "password"));
2496
+ if (password === "") {
2497
+ password = asString(get(single, "auth"));
2498
+ }
2499
+ const obfs = asString(get(single, "obfs"));
2500
+ const obfsPassword = asString(get(single, "obfs-password"));
2501
+ const sni = asString(get(single, "sni"));
2502
+ const fingerprint = asString(get(single, "fingerprint"));
2503
+ const alpnValue = get(single, "alpn");
2504
+ const alpn = Array.isArray(alpnValue) ? asString(alpnValue[0]) : asString(alpnValue);
2505
+ const ca = asString(get(single, "ca"));
2506
+ const caStr = asString(get(single, "ca-str"));
2507
+ const cwnd = asString(get(single, "cwnd"));
2508
+ const hopInterval = asString(get(single, "hop-interval"));
2509
+ hysteria2Construct(node, {
2510
+ alpn,
2511
+ ca,
2512
+ caStr,
2513
+ cwnd,
2514
+ down,
2515
+ fingerprint,
2516
+ group: group2,
2517
+ hopInterval,
2518
+ obfs,
2519
+ obfsPassword,
2520
+ password,
2521
+ port,
2522
+ ports,
2523
+ remark: ps,
2524
+ scv,
2525
+ server,
2526
+ sni,
2527
+ tfo,
2528
+ underlyingProxy,
2529
+ up
2530
+ });
2531
+ break;
2532
+ }
2533
+ case "anytls": {
2534
+ group2 = DEFAULT_GROUPS.anytls;
2535
+ const password = asString(get(single, "password"));
2536
+ const sni = asString(get(single, "sni"));
2537
+ anyTlsConstruct(node, {
2538
+ group: group2,
2539
+ password,
2540
+ port,
2541
+ remark: ps,
2542
+ scv,
2543
+ server,
2544
+ sni,
2545
+ tfo,
2546
+ udp,
2547
+ underlyingProxy
2548
+ });
2549
+ break;
2550
+ }
2551
+ case "vless": {
2552
+ group2 = DEFAULT_GROUPS.v2ray;
2553
+ const userId = asString(get(single, "uuid"));
2554
+ const net = asString(get(single, "network"));
2555
+ const tls = asString(get(single, "tls"));
2556
+ let path = "";
2557
+ let host = "";
2558
+ if (net === "ws") {
2559
+ path = asString(get(get(single, "ws-opts"), "path"));
2560
+ host = asString(get(get(get(single, "ws-opts"), "headers"), "Host"));
2561
+ } else if (net === "grpc") {
2562
+ path = asString(get(get(single, "grpc-opts"), "grpc-service-name"));
2563
+ }
2564
+ const alpnValue = get(single, "alpn");
2565
+ vlessConstruct(node, {
2566
+ alpn: Array.isArray(alpnValue) ? alpnValue.map((a) => String(a)) : void 0,
2567
+ encryption: asString(get(single, "encryption")),
2568
+ fingerprint: asString(get(single, "client-fingerprint")),
2569
+ flow: asString(get(single, "flow")),
2570
+ group: group2,
2571
+ host,
2572
+ network: net,
2573
+ path,
2574
+ port,
2575
+ remark: ps,
2576
+ scv,
2577
+ server,
2578
+ sni: asString(get(single, "servername")),
2579
+ tfo,
2580
+ tls: tls === "true" ? "tls" : "",
2581
+ udp,
2582
+ underlyingProxy,
2583
+ userId
2584
+ });
2585
+ break;
2586
+ }
2587
+ case "tuic": {
2588
+ group2 = DEFAULT_GROUPS.v2ray;
2589
+ const alpnValue = get(single, "alpn");
2590
+ tuicConstruct(node, {
2591
+ alpn: Array.isArray(alpnValue) ? alpnValue.map((a) => String(a)) : void 0,
2592
+ congestionControl: asString(get(single, "congestion-controller")),
2593
+ group: group2,
2594
+ password: asString(get(single, "password")),
2595
+ port,
2596
+ remark: ps,
2597
+ scv,
2598
+ server,
2599
+ sni: asString(get(single, "sni")),
2600
+ tfo,
2601
+ udp,
2602
+ underlyingProxy,
2603
+ userId: asString(get(single, "uuid"))
2604
+ });
2605
+ break;
2606
+ }
2607
+ default:
2608
+ return false;
2528
2609
  }
2610
+ node.id = index;
2611
+ nodes.push(node);
2612
+ return true;
2529
2613
  }
2530
2614
  function parseRawSections(text) {
2531
2615
  const sections = [];
@@ -4133,7 +4217,10 @@ function explodeSs(ss, node) {
4133
4217
  const secret = match[1];
4134
4218
  server = match[2];
4135
4219
  port = match[3];
4136
- const secretMatch = firstMatch(urlSafeBase64Decode(secret), "(\\S+?):(\\S+)");
4220
+ const raw = urlDecode(secret);
4221
+ const decoded = firstMatch(urlSafeBase64Decode(raw), "(\\S+?):(\\S+)");
4222
+ const plaintext = firstMatch(raw, "(\\S+?):(\\S+)");
4223
+ const secretMatch = decoded && isSsCipher(decoded[1]) ? decoded : plaintext ?? decoded;
4137
4224
  if (!secretMatch) {
4138
4225
  return;
4139
4226
  }
@@ -4598,6 +4685,157 @@ function explodeAnyTls(link, node) {
4598
4685
  udp: udp === "" ? void 0 : parseTri(udp)
4599
4686
  });
4600
4687
  }
4688
+ function explodeSnell(link, node) {
4689
+ let body = link.slice(8);
4690
+ let remark = "";
4691
+ let addition = "";
4692
+ const hashPos = body.lastIndexOf("#");
4693
+ if (hashPos !== -1) {
4694
+ remark = urlDecode(body.slice(hashPos + 1));
4695
+ body = body.slice(0, hashPos);
4696
+ }
4697
+ const qpos = body.lastIndexOf("?");
4698
+ if (qpos !== -1) {
4699
+ addition = body.slice(qpos + 1);
4700
+ body = body.slice(0, qpos);
4701
+ }
4702
+ const match = /^(.*?)@(.*)[:](\d+)$/.exec(body);
4703
+ if (!match) {
4704
+ return;
4705
+ }
4706
+ const password = match[1];
4707
+ const server = match[2];
4708
+ const port = match[3];
4709
+ if (password === "" || port === "" || port === "0") {
4710
+ return;
4711
+ }
4712
+ const obfs = getUrlArg(addition, "obfs");
4713
+ const host = getUrlArg(addition, "host");
4714
+ const version = getUrlArg(addition, "version");
4715
+ const udp = getUrlArg(addition, "udp");
4716
+ const tfo = getUrlArg(addition, "tfo");
4717
+ const scv = getUrlArg(addition, "skip-cert-verify");
4718
+ if (remark === "") {
4719
+ remark = `${server}:${port}`;
4720
+ }
4721
+ snellConstruct(node, {
4722
+ group: DEFAULT_GROUPS.snell,
4723
+ host,
4724
+ obfs: obfs === "" ? "none" : obfs,
4725
+ password,
4726
+ port,
4727
+ remark,
4728
+ scv: scv === "" ? void 0 : parseTri(scv),
4729
+ server,
4730
+ tfo: tfo === "" ? void 0 : parseTri(tfo),
4731
+ udp: udp === "" ? void 0 : parseTri(udp),
4732
+ version: version === "" ? 1 : toInt(version)
4733
+ });
4734
+ }
4735
+ function explodeHysteria(link, node) {
4736
+ let body = link.slice(11);
4737
+ let remark = "";
4738
+ let addition = "";
4739
+ const hashPos = body.lastIndexOf("#");
4740
+ if (hashPos !== -1) {
4741
+ remark = urlDecode(body.slice(hashPos + 1));
4742
+ body = body.slice(0, hashPos);
4743
+ }
4744
+ const qpos = body.lastIndexOf("?");
4745
+ if (qpos !== -1) {
4746
+ addition = body.slice(qpos + 1);
4747
+ body = body.slice(0, qpos);
4748
+ }
4749
+ const match = /^(.*)[:](\d+)$/.exec(body);
4750
+ if (!match) {
4751
+ return;
4752
+ }
4753
+ const server = match[1];
4754
+ const port = match[2];
4755
+ if (server === "" || port === "0") {
4756
+ return;
4757
+ }
4758
+ const auth = getUrlArg(addition, "auth");
4759
+ const protocol = getUrlArg(addition, "protocol");
4760
+ const upSpeed = getUrlArg(addition, "upmbps");
4761
+ const downSpeed = getUrlArg(addition, "downmbps");
4762
+ const alpn = getUrlArg(addition, "alpn");
4763
+ const obfs = getUrlArg(addition, "obfs");
4764
+ const obfsProtocol = getUrlArg(addition, "obfsParam");
4765
+ const sni = getUrlArg(addition, "peer");
4766
+ const insecure = getUrlArg(addition, "insecure");
4767
+ if (remark === "") {
4768
+ remark = `${server}:${port}`;
4769
+ }
4770
+ hysteriaConstruct(node, {
4771
+ alpn,
4772
+ authStr: auth,
4773
+ downSpeed,
4774
+ group: DEFAULT_GROUPS.hysteria,
4775
+ obfs,
4776
+ obfsProtocol,
4777
+ port,
4778
+ protocol: protocol === "" ? "udp" : protocol,
4779
+ remark,
4780
+ scv: insecure === "" ? void 0 : parseTri(insecure),
4781
+ server,
4782
+ sni: sni === "" ? server : sni,
4783
+ upSpeed
4784
+ });
4785
+ }
4786
+ function explodeWireguard(link, node) {
4787
+ const normalized = link.replace(/^(wg|wireguard):\/\//, "wireguard://");
4788
+ let body = normalized.slice(13);
4789
+ let remark = "";
4790
+ let addition = "";
4791
+ const hashPos = body.lastIndexOf("#");
4792
+ if (hashPos !== -1) {
4793
+ remark = urlDecode(body.slice(hashPos + 1));
4794
+ body = body.slice(0, hashPos);
4795
+ }
4796
+ const qpos = body.lastIndexOf("?");
4797
+ if (qpos !== -1) {
4798
+ addition = body.slice(qpos + 1);
4799
+ body = body.slice(0, qpos);
4800
+ }
4801
+ const match = /^(?:(.*)@)?\[?(.*?)]?:(\d+)$/.exec(body);
4802
+ if (!match) {
4803
+ return;
4804
+ }
4805
+ const server = match[2];
4806
+ const port = match[3];
4807
+ if (server === "" || port === "0") {
4808
+ return;
4809
+ }
4810
+ const privateKey = getUrlArg(addition, "privateKey") || getUrlArg(addition, "privatekey");
4811
+ const publicKey = getUrlArg(addition, "publicKey") || getUrlArg(addition, "publickey");
4812
+ if (privateKey === "" || publicKey === "") {
4813
+ return;
4814
+ }
4815
+ const presharedKey = getUrlArg(addition, "presharedKey");
4816
+ const selfIp = getUrlArg(addition, "selfIP");
4817
+ const selfIpv6 = getUrlArg(addition, "selfIPv6");
4818
+ const mtu = getUrlArg(addition, "mtu");
4819
+ const keepAlive = getUrlArg(addition, "keepAlive");
4820
+ const dns = getUrlArg(addition, "dns");
4821
+ if (remark === "") {
4822
+ remark = `${server}:${port}`;
4823
+ }
4824
+ wireGuardConstruct(node, {
4825
+ dnsServers: dns === "" ? ["1.1.1.1"] : dns.split(",").map((s) => s.trim()),
4826
+ group: DEFAULT_GROUPS.wireguard,
4827
+ keepAlive: keepAlive === "" ? 25 : toInt(keepAlive),
4828
+ mtu: mtu === "" ? 1420 : toInt(mtu),
4829
+ port,
4830
+ preSharedKey: presharedKey,
4831
+ privateKey,
4832
+ publicKey,
4833
+ remark,
4834
+ selfIp: selfIp === "" ? "10.0.0.2" : selfIp,
4835
+ selfIpv6,
4836
+ server
4837
+ });
4838
+ }
4601
4839
  function explodeVless(link, node) {
4602
4840
  let body = link.slice(8);
4603
4841
  let remark = "";
@@ -4721,6 +4959,12 @@ function explode(link, node) {
4721
4959
  explodeVless(link, node);
4722
4960
  } else if (startsWith(link, "tuic://")) {
4723
4961
  explodeTuic(link, node);
4962
+ } else if (startsWith(link, "snell://")) {
4963
+ explodeSnell(link, node);
4964
+ } else if (startsWith(link, "hysteria://")) {
4965
+ explodeHysteria(link, node);
4966
+ } else if (startsWith(link, "wg://") || startsWith(link, "wireguard://")) {
4967
+ explodeWireguard(link, node);
4724
4968
  } else if (link.includes("hysteria2://") || link.includes("hy2://")) {
4725
4969
  explodeHysteria2(link, node);
4726
4970
  } else if (startsWith(link, "anytls://")) {
@@ -5117,7 +5361,11 @@ function normalizeRegionCode(code) {
5117
5361
  const upper = code.trim().toUpperCase();
5118
5362
  return /^[A-Z]{2}$/.test(upper) ? upper : "";
5119
5363
  }
5120
- function regionDisplayName(code, locale = "zh-CN") {
5364
+ function normalizeCountryCode(code) {
5365
+ const upper = normalizeRegionCode(code);
5366
+ return upper !== "" && isRegionCode(upper) ? upper : void 0;
5367
+ }
5368
+ function displayName(code, locale) {
5121
5369
  const upper = normalizeRegionCode(code);
5122
5370
  if (upper === "") {
5123
5371
  return code.trim();
@@ -5160,8 +5408,8 @@ function buildAliasRegex() {
5160
5408
  }
5161
5409
  };
5162
5410
  for (const code of REGION_CODES) {
5163
- add(regionDisplayName(code, "zh-CN"));
5164
- add(regionDisplayName(code, "en"));
5411
+ add(displayName(code, "zh-CN"));
5412
+ add(displayName(code, "en"));
5165
5413
  add(regionFlag(code));
5166
5414
  }
5167
5415
  for (const alias of EXTRA_ALIASES) {
@@ -5201,13 +5449,13 @@ function hasRegionName(remark) {
5201
5449
  }
5202
5450
  return false;
5203
5451
  }
5204
- function formatCountryRemark(remark, code, locale = "zh-CN") {
5205
- const name = regionDisplayName(code, locale);
5452
+ function formatCountryRemark(remark, code) {
5453
+ const normalized = normalizeCountryCode(code);
5206
5454
  const trimmed = remark.trim();
5207
- if (name === "") {
5455
+ if (normalized === void 0) {
5208
5456
  return trimmed;
5209
5457
  }
5210
- return trimmed === "" ? name : `${name}-${trimmed}`;
5458
+ return trimmed === "" ? normalized : `${normalized}-${trimmed}`;
5211
5459
  }
5212
5460
 
5213
5461
  // src/generator/nodes.ts
@@ -5221,6 +5469,10 @@ function toRegex(pattern) {
5221
5469
  function regFind(value, pattern) {
5222
5470
  return toRegex(pattern).test(value);
5223
5471
  }
5472
+ var REGEX_BLACKLIST = ["(.*)*"];
5473
+ function isBlacklistedPattern(pattern) {
5474
+ return REGEX_BLACKLIST.includes(pattern);
5475
+ }
5224
5476
  function regMatch2(value, pattern) {
5225
5477
  const match = toRegex(pattern).exec(value);
5226
5478
  return match !== null && match[0] === value;
@@ -5466,7 +5718,7 @@ function nodeRename(node, renameArray) {
5466
5718
  node.remark = original;
5467
5719
  }
5468
5720
  }
5469
- function addCountryPrefix(node, resolve, locale = "zh-CN") {
5721
+ function addCountryPrefix(node, resolve) {
5470
5722
  const remark = node.remark.trim();
5471
5723
  if (hasRegionName(remark)) {
5472
5724
  return node.remark;
@@ -5475,7 +5727,31 @@ function addCountryPrefix(node, resolve, locale = "zh-CN") {
5475
5727
  if (code === void 0 || code === "") {
5476
5728
  return node.remark;
5477
5729
  }
5478
- return formatCountryRemark(remark, code, locale);
5730
+ return formatCountryRemark(remark, code);
5731
+ }
5732
+ function filterNodesByCountry(nodes, resolve, include, exclude) {
5733
+ const includeSet = new Set(
5734
+ include.map((code) => normalizeCountryCode(code)).filter((code) => code !== void 0)
5735
+ );
5736
+ const excludeSet = new Set(
5737
+ exclude.map((code) => normalizeCountryCode(code)).filter((code) => code !== void 0)
5738
+ );
5739
+ if (includeSet.size === 0 && excludeSet.size === 0) {
5740
+ return;
5741
+ }
5742
+ const kept = [];
5743
+ for (const node of nodes) {
5744
+ const code = resolve ? normalizeCountryCode(resolve(node) ?? "") : void 0;
5745
+ if (includeSet.size > 0 && (code === void 0 || !includeSet.has(code))) {
5746
+ continue;
5747
+ }
5748
+ if (code !== void 0 && excludeSet.has(code)) {
5749
+ continue;
5750
+ }
5751
+ kept.push(node);
5752
+ }
5753
+ nodes.length = 0;
5754
+ nodes.push(...kept);
5479
5755
  }
5480
5756
  function preprocessNodes(nodes, options) {
5481
5757
  for (const node of nodes) {
@@ -5493,13 +5769,25 @@ function preprocessNodes(nodes, options) {
5493
5769
  node.remark = addEmoji(node, options.emojiArray);
5494
5770
  }
5495
5771
  if (options.autoCountry && options.countryResolver) {
5496
- node.remark = addCountryPrefix(
5497
- node,
5498
- options.countryResolver,
5499
- options.countryLocale ?? "zh-CN"
5500
- );
5772
+ node.remark = addCountryPrefix(node, options.countryResolver);
5501
5773
  }
5502
5774
  }
5775
+ filterNodesByCountry(
5776
+ nodes,
5777
+ options.countryResolver,
5778
+ options.includeCountries ?? [],
5779
+ options.excludeCountries ?? []
5780
+ );
5781
+ const includeRemarks = options.includeRemarks ?? [];
5782
+ const excludeRemarks = options.excludeRemarks ?? [];
5783
+ if (includeRemarks.length > 0 || excludeRemarks.length > 0) {
5784
+ const kept = nodes.filter((node) => !checkIgnore(node, excludeRemarks, includeRemarks));
5785
+ kept.forEach((node, index) => {
5786
+ node.id = index;
5787
+ });
5788
+ nodes.length = 0;
5789
+ nodes.push(...kept);
5790
+ }
5503
5791
  if (options.sortFlag) {
5504
5792
  nodes.sort((a, b) => a.remark < b.remark ? -1 : a.remark > b.remark ? 1 : 0);
5505
5793
  }
@@ -5556,12 +5844,59 @@ function stripBrackets(value) {
5556
5844
  function toTri(value) {
5557
5845
  return value;
5558
5846
  }
5847
+ var MIHOMO_PROTOCOLS = /* @__PURE__ */ new Set([
5848
+ "ss" /* Shadowsocks */,
5849
+ "ssr" /* ShadowsocksR */,
5850
+ "vmess" /* VMess */,
5851
+ "trojan" /* Trojan */,
5852
+ "snell" /* Snell */,
5853
+ "http" /* HTTP */,
5854
+ "https" /* HTTPS */,
5855
+ "socks5" /* SOCKS5 */,
5856
+ "wireguard" /* WireGuard */,
5857
+ "hysteria" /* Hysteria */,
5858
+ "hysteria2" /* Hysteria2 */,
5859
+ "vless" /* VLESS */,
5860
+ "anytls" /* AnyTLS */,
5861
+ "tuic" /* TUIC */
5862
+ ]);
5863
+ var PREMIUM_PROTOCOLS = /* @__PURE__ */ new Set([
5864
+ "ss" /* Shadowsocks */,
5865
+ "ssr" /* ShadowsocksR */,
5866
+ "vmess" /* VMess */,
5867
+ "trojan" /* Trojan */,
5868
+ "snell" /* Snell */,
5869
+ "http" /* HTTP */,
5870
+ "https" /* HTTPS */,
5871
+ "socks5" /* SOCKS5 */,
5872
+ "wireguard" /* WireGuard */
5873
+ ]);
5874
+ var STASH_PROTOCOLS = /* @__PURE__ */ new Set([
5875
+ ...PREMIUM_PROTOCOLS,
5876
+ "hysteria" /* Hysteria */,
5877
+ "hysteria2" /* Hysteria2 */,
5878
+ "vless" /* VLESS */,
5879
+ "tuic" /* TUIC */
5880
+ ]);
5881
+ function clashCapabilities(flavor) {
5882
+ if (flavor === "premium") {
5883
+ return { clientFingerprint: false, protocols: PREMIUM_PROTOCOLS, reality: false };
5884
+ }
5885
+ if (flavor === "stash") {
5886
+ return { clientFingerprint: false, protocols: STASH_PROTOCOLS, reality: true };
5887
+ }
5888
+ return { clientFingerprint: true, protocols: MIHOMO_PROTOCOLS, reality: true };
5889
+ }
5559
5890
  function buildClashProxy(x, settings, clashR, remark, skipCertVerify, tfo) {
5560
5891
  const proxy = {
5561
5892
  name: remark,
5562
5893
  port: x.port,
5563
5894
  server: stripBrackets(x.hostname)
5564
5895
  };
5896
+ const caps = clashCapabilities(settings.clashFlavor);
5897
+ if (!caps.protocols.has(x.type)) {
5898
+ return void 0;
5899
+ }
5565
5900
  switch (x.type) {
5566
5901
  case "ss" /* Shadowsocks */: {
5567
5902
  if (settings.filterDeprecated && x.encryptMethod === "chacha20") {
@@ -5811,7 +6146,7 @@ function buildClashProxy(x, settings, clashR, remark, skipCertVerify, tfo) {
5811
6146
  if (skipCertVerify !== void 0) {
5812
6147
  proxy["skip-cert-verify"] = skipCertVerify;
5813
6148
  }
5814
- if (x.fingerprint !== "") {
6149
+ if (x.fingerprint !== "" && caps.clientFingerprint) {
5815
6150
  proxy.fingerprint = x.fingerprint;
5816
6151
  }
5817
6152
  if (x.alpn.length > 0) {
@@ -5866,7 +6201,7 @@ function buildClashProxy(x, settings, clashR, remark, skipCertVerify, tfo) {
5866
6201
  if (skipCertVerify !== void 0) {
5867
6202
  proxy["skip-cert-verify"] = skipCertVerify;
5868
6203
  }
5869
- if (x.fingerprint !== "") {
6204
+ if (x.fingerprint !== "" && caps.clientFingerprint) {
5870
6205
  proxy.fingerprint = x.fingerprint;
5871
6206
  }
5872
6207
  if (x.alpn.length > 0) {
@@ -5905,10 +6240,13 @@ function buildClashProxy(x, settings, clashR, remark, skipCertVerify, tfo) {
5905
6240
  if (x.serverName !== "") {
5906
6241
  proxy.servername = x.serverName;
5907
6242
  }
5908
- if (x.fingerprint !== "") {
6243
+ if (x.fingerprint !== "" && caps.clientFingerprint) {
5909
6244
  proxy["client-fingerprint"] = x.fingerprint;
5910
6245
  }
5911
6246
  if (x.publicKey !== "" || x.shortId !== "") {
6247
+ if (!caps.reality) {
6248
+ return void 0;
6249
+ }
5912
6250
  const realityOpts = {};
5913
6251
  if (x.publicKey !== "") {
5914
6252
  realityOpts["public-key"] = x.publicKey;
@@ -7265,7 +7603,7 @@ function collectRuleLines(content, group2, noResolveOnly) {
7265
7603
  }
7266
7604
  return out;
7267
7605
  }
7268
- function rulesetToClashRules(rulesets) {
7606
+ function rulesetToClashRules(rulesets, classical = false) {
7269
7607
  const rules = [];
7270
7608
  for (const ruleset of rulesets) {
7271
7609
  const content = ruleset.content;
@@ -7281,7 +7619,10 @@ function rulesetToClashRules(rulesets) {
7281
7619
  rules.push(transformRuleToCommon(line, group2));
7282
7620
  continue;
7283
7621
  }
7284
- const converted = convertRuleset(content, ruleset.ruleType);
7622
+ const converted = convertRuleset(
7623
+ content,
7624
+ classical && /^payload:\r?\n/.test(content) ? "clash-classical" /* ClashClassical */ : ruleset.ruleType
7625
+ );
7285
7626
  for (const rawLine of converted.split(/\r?\n/)) {
7286
7627
  let line = rawLine.trim();
7287
7628
  if (!isRuleLine(line)) {
@@ -7298,7 +7639,7 @@ function rulesetToClashRules(rulesets) {
7298
7639
  }
7299
7640
  return rules;
7300
7641
  }
7301
- function rulesetToSurgeRules(rulesets, surgeVersion) {
7642
+ function rulesetToSurgeRules(rulesets, surgeVersion, expand = true) {
7302
7643
  let section;
7303
7644
  let allowed;
7304
7645
  let noResolveOnly = false;
@@ -7336,6 +7677,17 @@ function rulesetToSurgeRules(rulesets, surgeVersion) {
7336
7677
  continue;
7337
7678
  }
7338
7679
  const group2 = ruleset.ruleGroup;
7680
+ if (!expand && (surgeVersion > 2 || surgeVersion === -3)) {
7681
+ const { url } = parseRulesetSource(ruleset.rulePath);
7682
+ if (/^https?:\/\//i.test(url.trim()) && !ruleset.content.startsWith("[]")) {
7683
+ let line = `RULE-SET,${url.trim()},${group2}`;
7684
+ if (ruleset.updateInterval > 0) {
7685
+ line += `,update-interval=${ruleset.updateInterval}`;
7686
+ }
7687
+ lines.push(line);
7688
+ continue;
7689
+ }
7690
+ }
7339
7691
  if (ruleset.content.startsWith("[]")) {
7340
7692
  let line = ruleset.content.slice(2);
7341
7693
  if (line === "MATCH") {
@@ -8100,9 +8452,24 @@ function buildOutbound(x, settings, remark) {
8100
8452
  proxy = out;
8101
8453
  break;
8102
8454
  }
8103
- // sing-box has no SSR support — drop such nodes.
8455
+ // sing-box has no ShadowsocksR implementation (configs using it fail
8456
+ // at startup), so such nodes are dropped.
8104
8457
  case "ssr" /* ShadowsocksR */:
8105
8458
  return void 0;
8459
+ case "snell" /* Snell */: {
8460
+ const out = { ...common, psk: x.password, type: "snell" };
8461
+ if (x.snellVersion) {
8462
+ out.version = x.snellVersion;
8463
+ }
8464
+ if (x.obfs !== "" && x.obfs !== "none") {
8465
+ out.obfs_mode = x.obfs;
8466
+ }
8467
+ if (x.host !== "") {
8468
+ out.obfs_host = x.host;
8469
+ }
8470
+ proxy = out;
8471
+ break;
8472
+ }
8106
8473
  case "vmess" /* VMess */: {
8107
8474
  const out = {
8108
8475
  ...common,
@@ -8385,9 +8752,12 @@ function runPreprocess(nodes, settings) {
8385
8752
  preprocessNodes(nodes, {
8386
8753
  addEmoji: settings.addEmoji,
8387
8754
  autoCountry: settings.autoCountry,
8388
- countryLocale: settings.countryLocale,
8389
8755
  countryResolver: settings.countryResolver,
8390
8756
  emojiArray: settings.emojiArray,
8757
+ excludeCountries: settings.excludeCountries,
8758
+ excludeRemarks: settings.excludeRemarks,
8759
+ includeCountries: settings.includeCountries,
8760
+ includeRemarks: settings.includeRemarks,
8391
8761
  nameRegexArray: settings.nameRegexArray,
8392
8762
  nameRemove: settings.nameRemove,
8393
8763
  removeEmoji: settings.removeEmoji,
@@ -8403,6 +8773,7 @@ function generate(nodes, options) {
8403
8773
  runPreprocess(nodes, settings);
8404
8774
  const groups = resolveGroups(options);
8405
8775
  const rulesets = resolveRulesets(options, settings);
8776
+ const baseConf = applyBaseTemplate(options.baseConf ?? "", settings);
8406
8777
  switch (options.target) {
8407
8778
  case "clash" /* Clash */:
8408
8779
  case "clashr" /* ClashR */: {
@@ -8412,9 +8783,9 @@ function generate(nodes, options) {
8412
8783
  }
8413
8784
  case "surge" /* Surge */: {
8414
8785
  const surgeVersion = options.surgeVersion ?? 5;
8415
- const rules = rulesets ? rulesetToSurgeRules(rulesets, surgeVersion) : void 0;
8786
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, surgeVersion, settings.expandRulesets) : void 0;
8416
8787
  return toSurge(nodes, groups, settings, {
8417
- baseConf: options.baseConf ?? "",
8788
+ baseConf,
8418
8789
  nodeList: settings.nodeList,
8419
8790
  rules,
8420
8791
  version: surgeVersion
@@ -8423,7 +8794,7 @@ function generate(nodes, options) {
8423
8794
  case "quanx" /* QuantumultX */: {
8424
8795
  const rules = rulesets ? rulesetToSurgeRules(rulesets, -1) : void 0;
8425
8796
  return toQuanX(nodes, groups, settings, {
8426
- baseConf: options.baseConf ?? "",
8797
+ baseConf,
8427
8798
  nodeList: settings.nodeList,
8428
8799
  rules
8429
8800
  });
@@ -8431,7 +8802,7 @@ function generate(nodes, options) {
8431
8802
  case "quan" /* Quantumult */: {
8432
8803
  const rules = rulesets ? rulesetToSurgeRules(rulesets, -2) : void 0;
8433
8804
  return toQuan(nodes, groups, settings, {
8434
- baseConf: options.baseConf ?? "",
8805
+ baseConf,
8435
8806
  nodeList: settings.nodeList,
8436
8807
  rules
8437
8808
  });
@@ -8439,21 +8810,21 @@ function generate(nodes, options) {
8439
8810
  case "loon" /* Loon */: {
8440
8811
  const rules = rulesets ? rulesetToSurgeRules(rulesets, -4) : void 0;
8441
8812
  return toLoon(nodes, groups, settings, {
8442
- baseConf: options.baseConf ?? "",
8813
+ baseConf,
8443
8814
  nodeList: settings.nodeList,
8444
8815
  rules
8445
8816
  });
8446
8817
  }
8447
8818
  case "singbox" /* SingBox */:
8448
8819
  return toSingBox(nodes, groups, settings, {
8449
- baseConf: options.baseConf ?? "",
8820
+ baseConf,
8450
8821
  nodeList: settings.nodeList,
8451
8822
  overwriteOriginalRules: settings.overwriteOriginalRules,
8452
8823
  rulesets
8453
8824
  });
8454
8825
  case "mellow" /* Mellow */:
8455
8826
  return toMellow(nodes, groups, settings, {
8456
- baseConf: options.baseConf ?? "",
8827
+ baseConf,
8457
8828
  nodeList: settings.nodeList,
8458
8829
  overwriteOriginalRules: settings.overwriteOriginalRules,
8459
8830
  rulesets
@@ -8471,11 +8842,11 @@ function generate(nodes, options) {
8471
8842
  case "ssd" /* SSD */:
8472
8843
  return proxyToSsd(nodes, "", "");
8473
8844
  case "sssub" /* SSSub */:
8474
- return proxyToSsSub(nodes, options.baseConf ?? "");
8845
+ return proxyToSsSub(nodes, baseConf);
8475
8846
  case "surfboard" /* Surfboard */: {
8476
- const rules = rulesets ? rulesetToSurgeRules(rulesets, options.surgeVersion ?? 5) : void 0;
8847
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, options.surgeVersion ?? 5, settings.expandRulesets) : void 0;
8477
8848
  return toSurfboard(nodes, groups, settings, {
8478
- baseConf: options.baseConf ?? "",
8849
+ baseConf,
8479
8850
  nodeList: settings.nodeList,
8480
8851
  rules
8481
8852
  });
@@ -8488,6 +8859,12 @@ function generate(nodes, options) {
8488
8859
  throw new Error(`Unsupported target format: ${options.target}`);
8489
8860
  }
8490
8861
  }
8862
+ function applyBaseTemplate(baseConf, settings) {
8863
+ if (baseConf === "" || !hasTemplatePlaceholders(baseConf)) {
8864
+ return baseConf;
8865
+ }
8866
+ return renderTemplate(baseConf, settings.templateVars);
8867
+ }
8491
8868
  function toSip008(nodes, baseConf = "") {
8492
8869
  return proxyToSsSub(nodes, baseConf);
8493
8870
  }
@@ -8517,7 +8894,7 @@ function attachClashRules(doc, settings, rulesets) {
8517
8894
  return;
8518
8895
  }
8519
8896
  const field = settings.clashNewFieldName ? "rules" : "Rule";
8520
- const generated = rulesetToClashRules(rulesets);
8897
+ const generated = rulesetToClashRules(rulesets, settings.clashClassicalRuleset);
8521
8898
  if (generated.length === 0) {
8522
8899
  return;
8523
8900
  }