@subconverter/core 0.1.0 → 0.2.1

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.js CHANGED
@@ -31,6 +31,23 @@ function createProxyGroupConfig() {
31
31
  usingProvider: []
32
32
  };
33
33
  }
34
+ function createSelectGroup(name) {
35
+ return {
36
+ disableUdp: void 0,
37
+ evaluateBeforeUse: void 0,
38
+ interval: 0,
39
+ lazy: void 0,
40
+ name,
41
+ persistent: void 0,
42
+ proxies: ["[]DIRECT", "!!TYPE=SS", "!!TYPE=SSR", "!!TYPE=VMESS", "!!TYPE=TROJAN"],
43
+ strategy: "consistent-hashing" /* ConsistentHashing */,
44
+ timeout: 0,
45
+ tolerance: 0,
46
+ type: "select" /* Select */,
47
+ url: "",
48
+ usingProvider: []
49
+ };
50
+ }
34
51
  function parseProxyGroupType(value) {
35
52
  const normalized = value.trim().toLowerCase();
36
53
  switch (normalized) {
@@ -170,6 +187,7 @@ function createProxy() {
170
187
  selfIp: "",
171
188
  selfIpv6: "",
172
189
  serverName: "",
190
+ shortId: "",
173
191
  snellVersion: 0,
174
192
  sni: "",
175
193
  tcpFastOpen: void 0,
@@ -205,6 +223,7 @@ var TargetFormat = /* @__PURE__ */ ((TargetFormat2) => {
205
223
  TargetFormat2["Clash"] = "clash";
206
224
  TargetFormat2["ClashR"] = "clashr";
207
225
  TargetFormat2["Surge"] = "surge";
226
+ TargetFormat2["Surfboard"] = "surfboard";
208
227
  TargetFormat2["QuantumultX"] = "quanx";
209
228
  TargetFormat2["Quantumult"] = "quan";
210
229
  TargetFormat2["Loon"] = "loon";
@@ -213,6 +232,7 @@ var TargetFormat = /* @__PURE__ */ ((TargetFormat2) => {
213
232
  TargetFormat2["Trojan"] = "trojan";
214
233
  TargetFormat2["SSD"] = "ssd";
215
234
  TargetFormat2["SSC"] = "ss";
235
+ TargetFormat2["SSSub"] = "sssub";
216
236
  TargetFormat2["SSR"] = "ssr";
217
237
  TargetFormat2["SingBox"] = "singbox";
218
238
  TargetFormat2["Mixed"] = "mixed";
@@ -225,16 +245,21 @@ function createExtraSettings() {
225
245
  addEmoji: false,
226
246
  appendProxyType: false,
227
247
  authorized: false,
248
+ autoCountry: false,
228
249
  clashClassicalRuleset: false,
229
- clashNewFieldName: false,
250
+ clashNewFieldName: true,
230
251
  clashProxiesStyle: "flow",
231
252
  clashProxyGroupsStyle: "flow",
232
253
  clashScript: false,
254
+ countryLocale: "zh-CN",
255
+ countryResolver: void 0,
233
256
  dedupe: false,
234
257
  emojiArray: [],
235
258
  enableRuleGenerator: true,
236
259
  filterDeprecated: false,
237
260
  managedConfigPrefix: "",
261
+ nameRegexArray: [],
262
+ nameRemove: [],
238
263
  nodeList: false,
239
264
  overwriteOriginalRules: true,
240
265
  quanxDevId: "",
@@ -540,27 +565,35 @@ function urlEncode(input) {
540
565
  (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
541
566
  );
542
567
  }
543
- function getUrlArg(query, key) {
568
+ function normalizeQueryKey(key) {
569
+ return key.toLowerCase().replace(/_/g, "-");
570
+ }
571
+ function getAllUrlArgs(query, key) {
544
572
  if (query === "") {
545
- return "";
546
- }
547
- const normalized = query.startsWith("?") ? query.slice(1) : query;
548
- const parts = normalized.split("&");
549
- let found = "";
550
- const target = key.toLowerCase();
551
- for (const part of parts) {
573
+ return [];
574
+ }
575
+ const hashCut = query.split("#", 1)[0];
576
+ const qIndex = hashCut.indexOf("?");
577
+ const rawQuery = qIndex >= 0 ? hashCut.slice(qIndex + 1) : hashCut;
578
+ const normalized = rawQuery.startsWith("?") ? rawQuery.slice(1) : rawQuery;
579
+ const values = [];
580
+ const target = normalizeQueryKey(key);
581
+ for (const part of normalized.split("&")) {
552
582
  if (part === "") {
553
583
  continue;
554
584
  }
555
585
  const eq = part.indexOf("=");
556
586
  const rawKey = eq >= 0 ? part.slice(0, eq) : part;
557
- if (rawKey.toLowerCase() !== target) {
587
+ if (normalizeQueryKey(urlDecode(rawKey)) !== target) {
558
588
  continue;
559
589
  }
560
- const rawValue = eq >= 0 ? part.slice(eq + 1) : "";
561
- found = urlDecode(rawValue);
590
+ values.push(urlDecode(eq >= 0 ? part.slice(eq + 1) : ""));
562
591
  }
563
- return found.trim();
592
+ return values;
593
+ }
594
+ function getUrlArg(query, key) {
595
+ const values = getAllUrlArgs(query, key);
596
+ return values.length > 0 ? values[values.length - 1].trim() : "";
564
597
  }
565
598
  function splitUrl(raw) {
566
599
  const hashIndex = raw.indexOf("#");
@@ -636,7 +669,7 @@ function parseCustomProxyGroup(value) {
636
669
  if (type === void 0) {
637
670
  return void 0;
638
671
  }
639
- const group = {
672
+ const group2 = {
640
673
  disableUdp: void 0,
641
674
  evaluateBeforeUse: void 0,
642
675
  interval: 0,
@@ -657,29 +690,29 @@ function parseCustomProxyGroup(value) {
657
690
  return void 0;
658
691
  }
659
692
  upperBound -= 2;
660
- group.url = parts[upperBound];
693
+ group2.url = parts[upperBound];
661
694
  const times = parseGroupTimes(parts[upperBound + 1]);
662
- group.interval = times.interval;
663
- group.timeout = times.timeout;
664
- group.tolerance = times.tolerance;
695
+ group2.interval = times.interval;
696
+ group2.timeout = times.timeout;
697
+ group2.tolerance = times.tolerance;
665
698
  }
666
699
  for (let i = 2; i < upperBound; i += 1) {
667
700
  const rule = parts[i];
668
701
  if (startsWith(rule, "!!PROVIDER=")) {
669
- group.usingProvider.push(...rule.slice(11).split(","));
702
+ group2.usingProvider.push(...rule.slice(11).split(","));
670
703
  } else {
671
- group.proxies.push(rule);
704
+ group2.proxies.push(rule);
672
705
  }
673
706
  }
674
- return group;
707
+ return group2;
675
708
  }
676
709
  function parseRulesetConfig(value) {
677
710
  const pos = value.indexOf(",");
678
711
  if (pos === -1) {
679
712
  return void 0;
680
713
  }
681
- const group = value.slice(0, pos);
682
- const config = { group, interval: 86400, url: "" };
714
+ const group2 = value.slice(0, pos);
715
+ const config = { group: group2, interval: 86400, url: "" };
683
716
  if (value.slice(pos + 1, pos + 3) === "[]") {
684
717
  config.url = value.slice(pos + 1);
685
718
  return config;
@@ -705,12 +738,15 @@ function parseRegexMatch(value, delimiter) {
705
738
  function parseExternalConfig(content) {
706
739
  const result = {
707
740
  addEmoji: false,
741
+ autoCountry: false,
708
742
  baseFiles: {},
709
743
  emoji: [],
710
744
  enableRuleGenerator: true,
711
745
  excludeRemarks: [],
712
746
  groups: [],
713
747
  includeRemarks: [],
748
+ nameRegex: [],
749
+ nameRemove: [],
714
750
  overwriteOriginalRules: true,
715
751
  removeEmoji: false,
716
752
  rename: [],
@@ -755,6 +791,17 @@ function parseExternalConfig(content) {
755
791
  case "rename_node":
756
792
  result.rename.push(parseRegexMatch(value, "@"));
757
793
  break;
794
+ case "name_regex":
795
+ result.nameRegex.push(parseRegexMatch(value, "@"));
796
+ break;
797
+ case "name_remove":
798
+ for (const fragment of value.split(",")) {
799
+ const trimmed = fragment.trim();
800
+ if (trimmed !== "") {
801
+ result.nameRemove.push(trimmed);
802
+ }
803
+ }
804
+ break;
758
805
  case "emoji":
759
806
  case "emojis":
760
807
  result.emoji.push(parseRegexMatch(value, ","));
@@ -774,6 +821,10 @@ function parseExternalConfig(content) {
774
821
  case "add_emoji":
775
822
  result.addEmoji = value.toLowerCase() === "true";
776
823
  break;
824
+ case "auto_country":
825
+ case "country":
826
+ result.autoCountry = value.toLowerCase() === "true";
827
+ break;
777
828
  case "remove_old_emoji":
778
829
  case "remove_emoji":
779
830
  result.removeEmoji = value.toLowerCase() === "true";
@@ -797,7 +848,13 @@ function parseExternalConfig(content) {
797
848
 
798
849
  // src/settings/request-params.ts
799
850
  var TARGETS = new Set(Object.values(TargetFormat));
851
+ function hasArg(query, key) {
852
+ return getAllUrlArgs(query, key).length > 0;
853
+ }
800
854
  function boolArg(query, key) {
855
+ if (!hasArg(query, key)) {
856
+ return false;
857
+ }
801
858
  const value = getUrlArg(query, key).toLowerCase();
802
859
  return value === "true" || value === "1" || value === "";
803
860
  }
@@ -809,13 +866,47 @@ function listArg(query, key) {
809
866
  const value = getUrlArg(query, key);
810
867
  return value === "" ? [] : value.split(",");
811
868
  }
869
+ function resolveNewFieldName(query) {
870
+ if (boolArg(query, "legacy") || boolArg(query, "legacy_field_name") || boolArg(query, "legacy-field-name")) {
871
+ return false;
872
+ }
873
+ if (hasArg(query, "new_name") || hasArg(query, "new_field_name") || hasArg(query, "new-name") || hasArg(query, "new-field-name")) {
874
+ return boolArg(query, "new_name") || boolArg(query, "new_field_name") || boolArg(query, "new-name") || boolArg(query, "new-field-name");
875
+ }
876
+ return true;
877
+ }
878
+ function firstArg(query, ...keys) {
879
+ for (const key of keys) {
880
+ const value = getUrlArg(query, key);
881
+ if (value !== "") {
882
+ return value;
883
+ }
884
+ }
885
+ return "";
886
+ }
887
+ function queryBoolFlag(query, ...keys) {
888
+ let found = false;
889
+ let result = false;
890
+ for (const key of keys) {
891
+ for (const raw of getAllUrlArgs(query, key)) {
892
+ found = true;
893
+ const normalized = raw.trim().toLowerCase();
894
+ if (normalized === "" || normalized === "true" || normalized === "1") {
895
+ result = true;
896
+ }
897
+ }
898
+ }
899
+ return found ? result : void 0;
900
+ }
812
901
  function createRequestSettings() {
813
902
  return {
814
903
  addEmoji: false,
815
904
  appendType: false,
905
+ autoCountry: false,
816
906
  autoExtract: false,
817
907
  clashScript: false,
818
908
  classicalRuleset: false,
909
+ dedupe: false,
819
910
  emoji: [],
820
911
  excludeRemarks: [],
821
912
  filterDeprecated: false,
@@ -825,7 +916,9 @@ function createRequestSettings() {
825
916
  interval: 0,
826
917
  list: false,
827
918
  mixin: "",
828
- newFieldName: false,
919
+ nameRegex: [],
920
+ nameRemove: [],
921
+ newFieldName: true,
829
922
  nodeList: false,
830
923
  proxiesStyle: "flow",
831
924
  proxyGroupsStyle: "flow",
@@ -840,7 +933,9 @@ function createRequestSettings() {
840
933
  tls13: void 0,
841
934
  udp: void 0,
842
935
  uploadPath: "",
843
- url: ""
936
+ url: "",
937
+ userinfoProvider: "",
938
+ userinfoUrl: ""
844
939
  };
845
940
  }
846
941
  function parseRequestSettings(query) {
@@ -852,11 +947,14 @@ function parseRequestSettings(query) {
852
947
  settings.url = getUrlArg(query, "url");
853
948
  settings.list = boolArg(query, "list");
854
949
  settings.nodeList = settings.list;
950
+ settings.sort = queryBoolFlag(query, "sort", "sort_flag", "sort-flag") ?? false;
951
+ settings.dedupe = queryBoolFlag(query, "dedupe", "deduplicate") ?? false;
855
952
  settings.addEmoji = boolArg(query, "add_emoji");
856
953
  settings.removeEmoji = boolArg(query, "remove_emoji");
954
+ settings.autoCountry = boolArg(query, "auto_country");
857
955
  settings.appendType = boolArg(query, "append_type");
858
956
  settings.filterDeprecated = boolArg(query, "filter_deprecated");
859
- settings.newFieldName = boolArg(query, "new_name") || boolArg(query, "new_field_name");
957
+ settings.newFieldName = resolveNewFieldName(query);
860
958
  settings.classicalRuleset = boolArg(query, "classical");
861
959
  settings.insert = getUrlArg(query, "insert") !== "";
862
960
  settings.clashScript = boolArg(query, "clash_script");
@@ -873,10 +971,20 @@ function parseRequestSettings(query) {
873
971
  settings.proxyGroupsStyle = getUrlArg(query, "proxy_groups_style") || "flow";
874
972
  settings.includeRemarks = listArg(query, "include");
875
973
  settings.excludeRemarks = listArg(query, "exclude");
974
+ settings.userinfoUrl = firstArg(query, "userinfo_url", "userinfo-url", "userinfo");
975
+ settings.userinfoProvider = firstArg(
976
+ query,
977
+ "userinfo_provider",
978
+ "userinfo-provider",
979
+ "userinfo_type",
980
+ "userinfo-type"
981
+ );
876
982
  const renameRaw = getUrlArg(query, "rename");
877
983
  if (renameRaw !== "") {
878
984
  settings.rename = [parseRegexMatch(renameRaw, "@")];
879
985
  }
986
+ settings.nameRegex = getAllUrlArgs(query, "name_regex").flatMap((raw) => raw.split("\n")).map((raw) => raw.trim()).filter((raw) => raw !== "").map((raw) => parseRegexMatch(raw, "@"));
987
+ settings.nameRemove = getAllUrlArgs(query, "name_remove").flatMap((raw) => raw.split(",")).map((raw) => raw.trim()).filter((raw) => raw !== "");
880
988
  const emojiRaw = getUrlArg(query, "emoji");
881
989
  if (emojiRaw !== "") {
882
990
  settings.emoji = [parseRegexMatch(emojiRaw, ",")];
@@ -884,6 +992,235 @@ function parseRequestSettings(query) {
884
992
  return settings;
885
993
  }
886
994
 
995
+ // src/userinfo.ts
996
+ var PROVIDER_ALIASES = {
997
+ auto: "auto",
998
+ clash: "subscription",
999
+ jms: "jms",
1000
+ "just-my-socks": "jms",
1001
+ justmysocks: "jms",
1002
+ raw: "subscription",
1003
+ sub: "subscription",
1004
+ subscription: "subscription",
1005
+ "subscription-userinfo": "subscription"
1006
+ };
1007
+ function normalizeUserinfoProvider(provider) {
1008
+ if (!provider) {
1009
+ return "auto";
1010
+ }
1011
+ const key = provider.trim().toLowerCase().replace(/_/g, "-");
1012
+ return PROVIDER_ALIASES[key] ?? "auto";
1013
+ }
1014
+ function inferUserinfoProviderFromUrl(url) {
1015
+ const squished = url.trim().toLowerCase().replace(/[-_]/g, "");
1016
+ if (squished.includes("justmysocks") || squished.includes("jmssub") || squished.includes("getbwcounter")) {
1017
+ return "jms";
1018
+ }
1019
+ return void 0;
1020
+ }
1021
+ function toNonNegativeNumber(value) {
1022
+ if (typeof value === "number") {
1023
+ return Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
1024
+ }
1025
+ if (typeof value === "string") {
1026
+ const parsed = toInt(value);
1027
+ return parsed > 0 ? parsed : 0;
1028
+ }
1029
+ return 0;
1030
+ }
1031
+ function daysInUtcMonth(year, month) {
1032
+ return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
1033
+ }
1034
+ var DECIMAL_TO_BINARY = 1024 ** 3 / 1e3 ** 3;
1035
+ function scaleDecimalBytes(value) {
1036
+ return Math.round(value * DECIMAL_TO_BINARY);
1037
+ }
1038
+ function scaleUserinfoDecimal(data) {
1039
+ return {
1040
+ download: scaleDecimalBytes(data.download),
1041
+ expire: data.expire,
1042
+ total: scaleDecimalBytes(data.total),
1043
+ upload: scaleDecimalBytes(data.upload)
1044
+ };
1045
+ }
1046
+ function nextMonthlyResetTimestamp(resetDay, nowSec) {
1047
+ const day = Math.trunc(resetDay);
1048
+ if (!Number.isFinite(day) || day < 1 || day > 31) {
1049
+ return 0;
1050
+ }
1051
+ const now = nowSec && nowSec > 0 ? Math.trunc(nowSec) : Math.trunc(Date.now() / 1e3);
1052
+ const current = new Date(now * 1e3);
1053
+ const year = current.getUTCFullYear();
1054
+ const month = current.getUTCMonth();
1055
+ const today = current.getUTCDate();
1056
+ const useCurrentMonth = today < day;
1057
+ const targetYear = useCurrentMonth ? year : month === 11 ? year + 1 : year;
1058
+ const targetMonth = useCurrentMonth ? month : (month + 1) % 12;
1059
+ const clampedDay = Math.min(day, daysInUtcMonth(targetYear, targetMonth));
1060
+ return Math.trunc(Date.UTC(targetYear, targetMonth, clampedDay) / 1e3);
1061
+ }
1062
+ function parseJmsUserinfo(raw, nowSec) {
1063
+ let parsed;
1064
+ try {
1065
+ parsed = JSON.parse(raw);
1066
+ } catch {
1067
+ return void 0;
1068
+ }
1069
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1070
+ return void 0;
1071
+ }
1072
+ const record = parsed;
1073
+ const hasJmsKeys = record["monthly_bw_limit_b"] !== void 0 || record["monthlyBwLimitB"] !== void 0 || record["bw_counter_b"] !== void 0 || record["bwCounterB"] !== void 0 || record["bw_reset_day_of_month"] !== void 0 || record["bwResetDayOfMonth"] !== void 0;
1074
+ if (!hasJmsKeys) {
1075
+ return void 0;
1076
+ }
1077
+ const limitRaw = record["monthly_bw_limit_b"] ?? record["monthlyBwLimitB"];
1078
+ const usedRaw = record["bw_counter_b"] ?? record["bwCounterB"];
1079
+ const resetRaw = record["bw_reset_day_of_month"] ?? record["bwResetDayOfMonth"] ?? record["reset_day"] ?? record["resetDay"];
1080
+ const payload = {
1081
+ bwCounterB: usedRaw,
1082
+ bwResetDayOfMonth: resetRaw,
1083
+ monthlyBwLimitB: limitRaw
1084
+ };
1085
+ const total = scaleDecimalBytes(toNonNegativeNumber(payload.monthlyBwLimitB));
1086
+ const download = scaleDecimalBytes(toNonNegativeNumber(payload.bwCounterB));
1087
+ const resetDay = toInt(
1088
+ typeof payload.bwResetDayOfMonth === "number" ? String(payload.bwResetDayOfMonth) : payload.bwResetDayOfMonth ?? ""
1089
+ );
1090
+ return {
1091
+ download,
1092
+ expire: resetDay > 0 ? nextMonthlyResetTimestamp(resetDay, nowSec) : 0,
1093
+ total,
1094
+ upload: 0
1095
+ };
1096
+ }
1097
+ function parseSubscriptionUserinfo(header) {
1098
+ if (!header || header.trim() === "") {
1099
+ return void 0;
1100
+ }
1101
+ const data = { download: 0, expire: 0, total: 0, upload: 0 };
1102
+ let found = false;
1103
+ for (const part of header.split(";")) {
1104
+ const eq = part.indexOf("=");
1105
+ if (eq === -1) {
1106
+ continue;
1107
+ }
1108
+ const key = part.slice(0, eq).trim().toLowerCase();
1109
+ const value = part.slice(eq + 1).trim();
1110
+ if (value === "") {
1111
+ continue;
1112
+ }
1113
+ switch (key) {
1114
+ case "download":
1115
+ case "down":
1116
+ data.download = toNonNegativeNumber(toInt(value));
1117
+ found = true;
1118
+ break;
1119
+ case "expire":
1120
+ case "expires":
1121
+ data.expire = toNonNegativeNumber(toInt(value));
1122
+ found = true;
1123
+ break;
1124
+ case "total":
1125
+ case "limit":
1126
+ data.total = toNonNegativeNumber(toInt(value));
1127
+ found = true;
1128
+ break;
1129
+ case "upload":
1130
+ case "up":
1131
+ data.upload = toNonNegativeNumber(toInt(value));
1132
+ found = true;
1133
+ break;
1134
+ default:
1135
+ break;
1136
+ }
1137
+ }
1138
+ return found ? data : void 0;
1139
+ }
1140
+ function formatSubscriptionUserinfo(data) {
1141
+ const upload = toNonNegativeNumber(data.upload);
1142
+ const download = toNonNegativeNumber(data.download);
1143
+ const total = toNonNegativeNumber(data.total);
1144
+ const expire = toNonNegativeNumber(data.expire);
1145
+ return `upload=${upload}; download=${download}; total=${total}; expire=${expire}`;
1146
+ }
1147
+ function parseGenericJsonUserinfo(raw) {
1148
+ let parsed;
1149
+ try {
1150
+ parsed = JSON.parse(raw);
1151
+ } catch {
1152
+ return void 0;
1153
+ }
1154
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1155
+ return void 0;
1156
+ }
1157
+ const record = parsed;
1158
+ const pick = (...keys) => {
1159
+ for (const key of keys) {
1160
+ if (record[key] !== void 0) {
1161
+ return record[key];
1162
+ }
1163
+ }
1164
+ return void 0;
1165
+ };
1166
+ const uploadRaw = pick("upload", "upload_b", "up", "u");
1167
+ const downloadRaw = pick(
1168
+ "download",
1169
+ "download_b",
1170
+ "down",
1171
+ "d",
1172
+ "used",
1173
+ "bw_counter_b",
1174
+ "bwCounterB"
1175
+ );
1176
+ const totalRaw = pick(
1177
+ "total",
1178
+ "total_b",
1179
+ "limit",
1180
+ "bandwidth",
1181
+ "monthly_bw_limit_b",
1182
+ "monthlyBwLimitB"
1183
+ );
1184
+ const expireRaw = pick("expire", "expires", "expire_at", "expireAt", "reset");
1185
+ if (uploadRaw === void 0 && downloadRaw === void 0 && totalRaw === void 0) {
1186
+ return void 0;
1187
+ }
1188
+ return {
1189
+ download: toNonNegativeNumber(downloadRaw),
1190
+ expire: toNonNegativeNumber(expireRaw),
1191
+ total: toNonNegativeNumber(totalRaw),
1192
+ upload: toNonNegativeNumber(uploadRaw)
1193
+ };
1194
+ }
1195
+ function convertUserinfo(raw, provider, nowSec) {
1196
+ if (!raw || raw.trim() === "") {
1197
+ return void 0;
1198
+ }
1199
+ const normalized = normalizeUserinfoProvider(provider);
1200
+ if (normalized === "jms") {
1201
+ const jms2 = parseJmsUserinfo(raw, nowSec);
1202
+ if (jms2) {
1203
+ return formatSubscriptionUserinfo(jms2);
1204
+ }
1205
+ const generic2 = parseGenericJsonUserinfo(raw);
1206
+ return generic2 ? formatSubscriptionUserinfo(scaleUserinfoDecimal(generic2)) : void 0;
1207
+ }
1208
+ if (normalized === "subscription") {
1209
+ const parsed = parseSubscriptionUserinfo(raw) ?? parseGenericJsonUserinfo(raw);
1210
+ return parsed ? formatSubscriptionUserinfo(parsed) : void 0;
1211
+ }
1212
+ const jms = parseJmsUserinfo(raw, nowSec);
1213
+ if (jms) {
1214
+ return formatSubscriptionUserinfo(jms);
1215
+ }
1216
+ const generic = parseGenericJsonUserinfo(raw);
1217
+ if (generic) {
1218
+ return formatSubscriptionUserinfo(generic);
1219
+ }
1220
+ const header = parseSubscriptionUserinfo(raw);
1221
+ return header ? formatSubscriptionUserinfo(header) : void 0;
1222
+ }
1223
+
887
1224
  // src/parser/ciphers.ts
888
1225
  var SS_CIPHERS = [
889
1226
  "2022-blake3-aes-128-gcm",
@@ -943,9 +1280,9 @@ function isFlattenableSsr(method, obfs, protocol) {
943
1280
  }
944
1281
 
945
1282
  // src/parser/construct.ts
946
- function commonConstruct(node, type, group, remark, server, port, udp, tfo, scv, tls13, underlyingProxy) {
1283
+ function commonConstruct(node, type, group2, remark, server, port, udp, tfo, scv, tls13, underlyingProxy) {
947
1284
  node.type = type;
948
- node.group = group;
1285
+ node.group = group2;
949
1286
  node.remark = remark;
950
1287
  node.hostname = server;
951
1288
  node.underlyingProxy = underlyingProxy;
@@ -961,7 +1298,7 @@ function vmessConstruct(node, options) {
961
1298
  aid = 0,
962
1299
  cipher = "",
963
1300
  edge = "",
964
- group,
1301
+ group: group2,
965
1302
  host = "",
966
1303
  id = "",
967
1304
  net = "",
@@ -980,7 +1317,7 @@ function vmessConstruct(node, options) {
980
1317
  commonConstruct(
981
1318
  node,
982
1319
  "vmess" /* VMess */,
983
- group,
1320
+ group2,
984
1321
  remark,
985
1322
  add,
986
1323
  port,
@@ -1277,6 +1614,8 @@ function vlessConstruct(node, options) {
1277
1614
  node.host = options.host ?? "";
1278
1615
  node.path = options.path ?? "";
1279
1616
  node.fingerprint = options.fingerprint ?? "";
1617
+ node.publicKey = options.publicKey ?? "";
1618
+ node.shortId = options.shortId ?? "";
1280
1619
  if (options.alpn) {
1281
1620
  node.alpn = options.alpn;
1282
1621
  }
@@ -1326,7 +1665,7 @@ function explodeNetch(netch, node) {
1326
1665
  return;
1327
1666
  }
1328
1667
  const type = str(data.Type);
1329
- const group = str(data.Group);
1668
+ const group2 = str(data.Group);
1330
1669
  let remark = str(data.Remark);
1331
1670
  const address = str(data.Hostname);
1332
1671
  const udp = tri(data.EnableUDP);
@@ -1344,7 +1683,7 @@ function explodeNetch(netch, node) {
1344
1683
  switch (type) {
1345
1684
  case "SS":
1346
1685
  ssConstruct(node, {
1347
- group: group === "" ? DEFAULT_GROUPS.ss : group,
1686
+ group: group2 === "" ? DEFAULT_GROUPS.ss : group2,
1348
1687
  method,
1349
1688
  password,
1350
1689
  plugin: str(data.Plugin),
@@ -1362,7 +1701,7 @@ function explodeNetch(netch, node) {
1362
1701
  const obfs = str(data.OBFS);
1363
1702
  if (isFlattenableSsr(method, obfs, protocol)) {
1364
1703
  ssConstruct(node, {
1365
- group: group === "" ? DEFAULT_GROUPS.ss : group,
1704
+ group: group2 === "" ? DEFAULT_GROUPS.ss : group2,
1366
1705
  method,
1367
1706
  password,
1368
1707
  plugin: str(data.Plugin),
@@ -1376,7 +1715,7 @@ function explodeNetch(netch, node) {
1376
1715
  });
1377
1716
  } else {
1378
1717
  ssrConstruct(node, {
1379
- group: group === "" ? DEFAULT_GROUPS.ssr : group,
1718
+ group: group2 === "" ? DEFAULT_GROUPS.ssr : group2,
1380
1719
  method,
1381
1720
  obfs,
1382
1721
  obfsParam: str(data.OBFSParam),
@@ -1399,7 +1738,7 @@ function explodeNetch(netch, node) {
1399
1738
  aid: str(data.AlterID),
1400
1739
  cipher: method,
1401
1740
  edge: str(data.Edge),
1402
- group: group === "" ? DEFAULT_GROUPS.v2ray : group,
1741
+ group: group2 === "" ? DEFAULT_GROUPS.v2ray : group2,
1403
1742
  host: str(data.Host),
1404
1743
  id: str(data.UserID),
1405
1744
  net: str(data.TransferProtocol),
@@ -1416,7 +1755,7 @@ function explodeNetch(netch, node) {
1416
1755
  break;
1417
1756
  case "Socks5":
1418
1757
  socksConstruct(node, {
1419
- group: group === "" ? DEFAULT_GROUPS.socks : group,
1758
+ group: group2 === "" ? DEFAULT_GROUPS.socks : group2,
1420
1759
  password,
1421
1760
  port,
1422
1761
  remark,
@@ -1430,7 +1769,7 @@ function explodeNetch(netch, node) {
1430
1769
  case "HTTP":
1431
1770
  case "HTTPS":
1432
1771
  httpConstruct(node, {
1433
- group: group === "" ? DEFAULT_GROUPS.http : group,
1772
+ group: group2 === "" ? DEFAULT_GROUPS.http : group2,
1434
1773
  password,
1435
1774
  port,
1436
1775
  remark,
@@ -1442,7 +1781,7 @@ function explodeNetch(netch, node) {
1442
1781
  break;
1443
1782
  case "Trojan":
1444
1783
  trojanConstruct(node, {
1445
- group: group === "" ? DEFAULT_GROUPS.trojan : group,
1784
+ group: group2 === "" ? DEFAULT_GROUPS.trojan : group2,
1446
1785
  host: str(data.Host),
1447
1786
  network: str(data.TransferProtocol),
1448
1787
  password,
@@ -1458,7 +1797,7 @@ function explodeNetch(netch, node) {
1458
1797
  break;
1459
1798
  case "Snell":
1460
1799
  snellConstruct(node, {
1461
- group: group === "" ? DEFAULT_GROUPS.snell : group,
1800
+ group: group2 === "" ? DEFAULT_GROUPS.snell : group2,
1462
1801
  host: str(data.Host),
1463
1802
  obfs: str(data.OBFS),
1464
1803
  password,
@@ -1522,10 +1861,10 @@ function explodeClash(root, nodes) {
1522
1861
  const tfo = asTri(get(single, "fast-open"));
1523
1862
  const scv = asTri(get(single, "skip-cert-verify"));
1524
1863
  const node = createProxy();
1525
- let group = "";
1864
+ let group2 = "";
1526
1865
  switch (proxyType) {
1527
1866
  case "vmess": {
1528
- group = DEFAULT_GROUPS.v2ray;
1867
+ group2 = DEFAULT_GROUPS.v2ray;
1529
1868
  const id = asString(get(single, "uuid"));
1530
1869
  const aid = asString(get(single, "alterId"));
1531
1870
  const cipher = asString(get(single, "cipher"));
@@ -1571,7 +1910,7 @@ function explodeClash(root, nodes) {
1571
1910
  aid,
1572
1911
  cipher,
1573
1912
  edge,
1574
- group,
1913
+ group: group2,
1575
1914
  host,
1576
1915
  id,
1577
1916
  net,
@@ -1589,7 +1928,7 @@ function explodeClash(root, nodes) {
1589
1928
  break;
1590
1929
  }
1591
1930
  case "ss": {
1592
- group = DEFAULT_GROUPS.ss;
1931
+ group2 = DEFAULT_GROUPS.ss;
1593
1932
  let cipher = asString(get(single, "cipher"));
1594
1933
  const password = asString(get(single, "password"));
1595
1934
  let plugin = "";
@@ -1640,7 +1979,7 @@ function explodeClash(root, nodes) {
1640
1979
  cipher = cipher.replace(/AEAD_/g, "").replace(/_/g, "-").toLowerCase();
1641
1980
  }
1642
1981
  ssConstruct(node, {
1643
- group,
1982
+ group: group2,
1644
1983
  method: cipher,
1645
1984
  password,
1646
1985
  plugin,
@@ -1656,11 +1995,11 @@ function explodeClash(root, nodes) {
1656
1995
  break;
1657
1996
  }
1658
1997
  case "socks5": {
1659
- group = DEFAULT_GROUPS.socks;
1998
+ group2 = DEFAULT_GROUPS.socks;
1660
1999
  const user = asString(get(single, "username"));
1661
2000
  const password = asString(get(single, "password"));
1662
2001
  socksConstruct(node, {
1663
- group,
2002
+ group: group2,
1664
2003
  password,
1665
2004
  port,
1666
2005
  remark: ps,
@@ -1671,7 +2010,7 @@ function explodeClash(root, nodes) {
1671
2010
  break;
1672
2011
  }
1673
2012
  case "ssr": {
1674
- group = DEFAULT_GROUPS.ssr;
2013
+ group2 = DEFAULT_GROUPS.ssr;
1675
2014
  let cipher = asString(get(single, "cipher"));
1676
2015
  if (cipher === "dummy") {
1677
2016
  cipher = "none";
@@ -1682,7 +2021,7 @@ function explodeClash(root, nodes) {
1682
2021
  const protocolParam = get(single, "protocol-param") !== void 0 ? asString(get(single, "protocol-param")) : asString(get(single, "protocolparam"));
1683
2022
  const obfsParam = get(single, "obfs-param") !== void 0 ? asString(get(single, "obfs-param")) : asString(get(single, "obfsparam"));
1684
2023
  ssrConstruct(node, {
1685
- group,
2024
+ group: group2,
1686
2025
  method: cipher,
1687
2026
  obfs,
1688
2027
  obfsParam,
@@ -1700,12 +2039,12 @@ function explodeClash(root, nodes) {
1700
2039
  break;
1701
2040
  }
1702
2041
  case "http": {
1703
- group = DEFAULT_GROUPS.http;
2042
+ group2 = DEFAULT_GROUPS.http;
1704
2043
  const user = asString(get(single, "username"));
1705
2044
  const password = asString(get(single, "password"));
1706
2045
  const tls = asString(get(single, "tls"));
1707
2046
  httpConstruct(node, {
1708
- group,
2047
+ group: group2,
1709
2048
  password,
1710
2049
  port,
1711
2050
  remark: ps,
@@ -1719,7 +2058,7 @@ function explodeClash(root, nodes) {
1719
2058
  break;
1720
2059
  }
1721
2060
  case "trojan": {
1722
- group = DEFAULT_GROUPS.trojan;
2061
+ group2 = DEFAULT_GROUPS.trojan;
1723
2062
  const password = asString(get(single, "password"));
1724
2063
  const host = asString(get(single, "sni"));
1725
2064
  let net = asString(get(single, "network"));
@@ -1737,7 +2076,7 @@ function explodeClash(root, nodes) {
1737
2076
  break;
1738
2077
  }
1739
2078
  trojanConstruct(node, {
1740
- group,
2079
+ group: group2,
1741
2080
  host,
1742
2081
  network: net,
1743
2082
  password,
@@ -1754,13 +2093,13 @@ function explodeClash(root, nodes) {
1754
2093
  break;
1755
2094
  }
1756
2095
  case "snell": {
1757
- group = DEFAULT_GROUPS.snell;
2096
+ group2 = DEFAULT_GROUPS.snell;
1758
2097
  const password = asString(get(single, "psk"));
1759
2098
  const obfs = asString(get(get(single, "obfs-opts"), "mode"));
1760
2099
  const host = asString(get(get(single, "obfs-opts"), "host"));
1761
2100
  const version = asString(get(single, "version"));
1762
2101
  snellConstruct(node, {
1763
- group,
2102
+ group: group2,
1764
2103
  host,
1765
2104
  obfs,
1766
2105
  password,
@@ -1776,7 +2115,7 @@ function explodeClash(root, nodes) {
1776
2115
  break;
1777
2116
  }
1778
2117
  case "wireguard": {
1779
- group = DEFAULT_GROUPS.wireguard;
2118
+ group2 = DEFAULT_GROUPS.wireguard;
1780
2119
  const publicKey = asString(get(single, "public-key"));
1781
2120
  const privateKey = asString(get(single, "private-key"));
1782
2121
  const dns = get(single, "dns");
@@ -1787,7 +2126,7 @@ function explodeClash(root, nodes) {
1787
2126
  const ipv6 = asString(get(single, "ipv6"));
1788
2127
  wireGuardConstruct(node, {
1789
2128
  dnsServers,
1790
- group,
2129
+ group: group2,
1791
2130
  keepAlive: "0",
1792
2131
  mtu,
1793
2132
  port,
@@ -1805,7 +2144,7 @@ function explodeClash(root, nodes) {
1805
2144
  break;
1806
2145
  }
1807
2146
  case "hysteria": {
1808
- group = DEFAULT_GROUPS.hysteria;
2147
+ group2 = DEFAULT_GROUPS.hysteria;
1809
2148
  const ports = asString(get(single, "ports"));
1810
2149
  const protocol = asString(get(single, "protocol"));
1811
2150
  const obfsProtocol = asString(get(single, "obfs-protocol"));
@@ -1842,7 +2181,7 @@ function explodeClash(root, nodes) {
1842
2181
  down,
1843
2182
  downSpeed,
1844
2183
  fingerprint,
1845
- group,
2184
+ group: group2,
1846
2185
  hopInterval,
1847
2186
  obfs,
1848
2187
  obfsProtocol,
@@ -1863,7 +2202,7 @@ function explodeClash(root, nodes) {
1863
2202
  break;
1864
2203
  }
1865
2204
  case "hysteria2": {
1866
- group = DEFAULT_GROUPS.hysteria2;
2205
+ group2 = DEFAULT_GROUPS.hysteria2;
1867
2206
  const ports = asString(get(single, "ports"));
1868
2207
  const up = asString(get(single, "up"));
1869
2208
  const down = asString(get(single, "down"));
@@ -1888,7 +2227,7 @@ function explodeClash(root, nodes) {
1888
2227
  cwnd,
1889
2228
  down,
1890
2229
  fingerprint,
1891
- group,
2230
+ group: group2,
1892
2231
  hopInterval,
1893
2232
  obfs,
1894
2233
  obfsPassword,
@@ -1906,11 +2245,11 @@ function explodeClash(root, nodes) {
1906
2245
  break;
1907
2246
  }
1908
2247
  case "anytls": {
1909
- group = DEFAULT_GROUPS.anytls;
2248
+ group2 = DEFAULT_GROUPS.anytls;
1910
2249
  const password = asString(get(single, "password"));
1911
2250
  const sni = asString(get(single, "sni"));
1912
2251
  anyTlsConstruct(node, {
1913
- group,
2252
+ group: group2,
1914
2253
  password,
1915
2254
  port,
1916
2255
  remark: ps,
@@ -1924,7 +2263,7 @@ function explodeClash(root, nodes) {
1924
2263
  break;
1925
2264
  }
1926
2265
  case "vless": {
1927
- group = DEFAULT_GROUPS.v2ray;
2266
+ group2 = DEFAULT_GROUPS.v2ray;
1928
2267
  const userId = asString(get(single, "uuid"));
1929
2268
  const net = asString(get(single, "network"));
1930
2269
  const tls = asString(get(single, "tls"));
@@ -1942,7 +2281,7 @@ function explodeClash(root, nodes) {
1942
2281
  encryption: asString(get(single, "encryption")),
1943
2282
  fingerprint: asString(get(single, "client-fingerprint")),
1944
2283
  flow: asString(get(single, "flow")),
1945
- group,
2284
+ group: group2,
1946
2285
  host,
1947
2286
  network: net,
1948
2287
  path,
@@ -1960,12 +2299,12 @@ function explodeClash(root, nodes) {
1960
2299
  break;
1961
2300
  }
1962
2301
  case "tuic": {
1963
- group = DEFAULT_GROUPS.v2ray;
2302
+ group2 = DEFAULT_GROUPS.v2ray;
1964
2303
  const alpnValue = get(single, "alpn");
1965
2304
  tuicConstruct(node, {
1966
2305
  alpn: Array.isArray(alpnValue) ? alpnValue.map((a) => String(a)) : void 0,
1967
2306
  congestionControl: asString(get(single, "congestion-controller")),
1968
- group,
2307
+ group: group2,
1969
2308
  password: asString(get(single, "password")),
1970
2309
  port,
1971
2310
  remark: ps,
@@ -2032,8 +2371,8 @@ function collectKeyValues(lines, startIndex, sink) {
2032
2371
  sink[trim(line.slice(0, eq))] = trim(line.slice(eq + 1));
2033
2372
  }
2034
2373
  }
2035
- function explodeSurge(surge, nodes) {
2036
- let text = surge;
2374
+ function explodeSurge(surge2, nodes) {
2375
+ let text = surge2;
2037
2376
  if (text.includes("[Proxy]")) {
2038
2377
  text = text.replace(/^[\s\S]*?\[/, "[");
2039
2378
  }
@@ -2858,7 +3197,7 @@ function explodeSsConf(content, nodes) {
2858
3197
  if (!(section in json)) {
2859
3198
  return;
2860
3199
  }
2861
- const group = asString(json.remarks);
3200
+ const group2 = asString(json.remarks);
2862
3201
  const list = json[section];
2863
3202
  if (!Array.isArray(list)) {
2864
3203
  return;
@@ -2873,7 +3212,7 @@ function explodeSsConf(content, nodes) {
2873
3212
  const server = asString(entry.server);
2874
3213
  const node = createProxy();
2875
3214
  ssConstruct(node, {
2876
- group,
3215
+ group: group2,
2877
3216
  method: asString(entry.method),
2878
3217
  password: asString(entry.password),
2879
3218
  plugin: asString(entry.plugin),
@@ -2978,7 +3317,7 @@ function explodeSsrConf(content, nodes) {
2978
3317
  return;
2979
3318
  }
2980
3319
  for (const entry of list) {
2981
- const group = asString(entry.group) === "" ? DEFAULT_GROUPS.ssr : asString(entry.group);
3320
+ const group2 = asString(entry.group) === "" ? DEFAULT_GROUPS.ssr : asString(entry.group);
2982
3321
  const server = asString(entry.server);
2983
3322
  const port = asString(entry.server_port);
2984
3323
  if (port === "0" || port === "") {
@@ -2987,7 +3326,7 @@ function explodeSsrConf(content, nodes) {
2987
3326
  const remark = asString(entry.remarks) === "" ? `${server}:${port}` : asString(entry.remarks);
2988
3327
  const node = createProxy();
2989
3328
  ssrConstruct(node, {
2990
- group,
3329
+ group: group2,
2991
3330
  method: asString(entry.method),
2992
3331
  obfs: asString(entry.obfs),
2993
3332
  obfsParam: asString(entry.obfsparam),
@@ -3092,9 +3431,9 @@ function explodeVmessConf(content, nodes) {
3092
3431
  continue;
3093
3432
  }
3094
3433
  const subid = asString(entry.subid);
3095
- let group = "";
3434
+ let group2 = "";
3096
3435
  if (subid !== "") {
3097
- group = subdata.get(subid) ?? "";
3436
+ group2 = subdata.get(subid) ?? "";
3098
3437
  }
3099
3438
  if (ps === "") {
3100
3439
  ps = `${add}:${port}`;
@@ -3107,7 +3446,7 @@ function explodeVmessConf(content, nodes) {
3107
3446
  add,
3108
3447
  aid: asString(entry.alterId),
3109
3448
  cipher: asString(entry.security),
3110
- group: group || DEFAULT_GROUPS.v2ray,
3449
+ group: group2 || DEFAULT_GROUPS.v2ray,
3111
3450
  host: asString(entry.requestHost),
3112
3451
  id: asString(entry.id),
3113
3452
  net: asString(entry.network),
@@ -3121,7 +3460,7 @@ function explodeVmessConf(content, nodes) {
3121
3460
  });
3122
3461
  } else if (configType === 3) {
3123
3462
  ssConstruct(node, {
3124
- group: group || DEFAULT_GROUPS.ss,
3463
+ group: group2 || DEFAULT_GROUPS.ss,
3125
3464
  method: asString(entry.security),
3126
3465
  password: asString(entry.id),
3127
3466
  port,
@@ -3131,7 +3470,7 @@ function explodeVmessConf(content, nodes) {
3131
3470
  });
3132
3471
  } else if (configType === 4) {
3133
3472
  socksConstruct(node, {
3134
- group: group || DEFAULT_GROUPS.socks,
3473
+ group: group2 || DEFAULT_GROUPS.socks,
3135
3474
  port,
3136
3475
  remark: ps,
3137
3476
  scv,
@@ -3157,7 +3496,7 @@ function explodeSstap(content, nodes) {
3157
3496
  }
3158
3497
  let index = nodes.length;
3159
3498
  for (const entry of json.configs) {
3160
- const group = asString(entry.group);
3499
+ const group2 = asString(entry.group);
3161
3500
  const server = asString(entry.server);
3162
3501
  const port = asString(entry.server_port);
3163
3502
  if (port === "0" || port === "") {
@@ -3169,7 +3508,7 @@ function explodeSstap(content, nodes) {
3169
3508
  const configType = toInt(entry.type);
3170
3509
  if (configType === 5) {
3171
3510
  socksConstruct(node, {
3172
- group,
3511
+ group: group2,
3173
3512
  password,
3174
3513
  port,
3175
3514
  remark,
@@ -3181,10 +3520,10 @@ function explodeSstap(content, nodes) {
3181
3520
  const obfs = asString(entry.obfs);
3182
3521
  const method = asString(entry.method);
3183
3522
  if (isFlattenableSsr(method, obfs, protocol)) {
3184
- ssConstruct(node, { group, method, password, port, remark, server });
3523
+ ssConstruct(node, { group: group2, method, password, port, remark, server });
3185
3524
  } else {
3186
3525
  ssrConstruct(node, {
3187
- group,
3526
+ group: group2,
3188
3527
  method,
3189
3528
  obfs,
3190
3529
  obfsParam: asString(entry.obfsparam),
@@ -3490,7 +3829,7 @@ function explodeQuan(quan, node) {
3490
3829
  }
3491
3830
  const cipher = trim(configs[4]);
3492
3831
  const id = trim(configs[5].replace(/"/g, ""));
3493
- let group = DEFAULT_GROUPS.v2ray;
3832
+ let group2 = DEFAULT_GROUPS.v2ray;
3494
3833
  let tls = "";
3495
3834
  let host = "";
3496
3835
  let path = "";
@@ -3505,7 +3844,7 @@ function explodeQuan(quan, node) {
3505
3844
  const itemVal = trim(vArray.slice(1).join("="));
3506
3845
  switch (itemName) {
3507
3846
  case "group":
3508
- group = itemVal;
3847
+ group2 = itemVal;
3509
3848
  break;
3510
3849
  case "over-tls":
3511
3850
  tls = itemVal === "true" ? "tls" : "";
@@ -3544,7 +3883,7 @@ function explodeQuan(quan, node) {
3544
3883
  aid: "0",
3545
3884
  cipher,
3546
3885
  edge,
3547
- group,
3886
+ group: group2,
3548
3887
  host,
3549
3888
  id,
3550
3889
  net,
@@ -3557,12 +3896,12 @@ function explodeQuan(quan, node) {
3557
3896
  }
3558
3897
  function explodeSs(ss, node) {
3559
3898
  let body = ss.slice(5).replace(/\/\?/g, "?");
3560
- const group = DEFAULT_GROUPS.ss;
3899
+ const group2 = DEFAULT_GROUPS.ss;
3561
3900
  let remark = "";
3562
3901
  let addition = "";
3563
3902
  let plugin = "";
3564
3903
  let pluginOption = "";
3565
- let resolvedGroup = group;
3904
+ let resolvedGroup = group2;
3566
3905
  const hashPos = body.indexOf("#");
3567
3906
  if (hashPos !== -1) {
3568
3907
  remark = urlDecode(body.slice(hashPos + 1));
@@ -3629,7 +3968,7 @@ function explodeSs(ss, node) {
3629
3968
  function explodeSsr(ssr, node) {
3630
3969
  let body = ssr.slice(6).replace(/\r/g, "");
3631
3970
  body = urlSafeBase64Decode(body);
3632
- let group = "";
3971
+ let group2 = "";
3633
3972
  let remark = "";
3634
3973
  let obfsParam = "";
3635
3974
  let protocolParam = "";
@@ -3637,7 +3976,7 @@ function explodeSsr(ssr, node) {
3637
3976
  if (obfsPos !== -1) {
3638
3977
  const strobfs = body.slice(obfsPos + 2);
3639
3978
  body = body.slice(0, obfsPos);
3640
- group = urlSafeBase64Decode(getUrlArg(strobfs, "group"));
3979
+ group2 = urlSafeBase64Decode(getUrlArg(strobfs, "group"));
3641
3980
  remark = urlSafeBase64Decode(getUrlArg(strobfs, "remarks"));
3642
3981
  obfsParam = urlSafeBase64Decode(getUrlArg(strobfs, "obfsparam")).replace(/\s/g, "");
3643
3982
  protocolParam = urlSafeBase64Decode(getUrlArg(strobfs, "protoparam")).replace(/\s/g, "");
@@ -3655,7 +3994,7 @@ function explodeSsr(ssr, node) {
3655
3994
  if (port === "0") {
3656
3995
  return;
3657
3996
  }
3658
- const resolvedGroup = group === "" ? DEFAULT_GROUPS.ssr : group;
3997
+ const resolvedGroup = group2 === "" ? DEFAULT_GROUPS.ssr : group2;
3659
3998
  const resolvedRemark = remark === "" ? `${server}:${port}` : remark;
3660
3999
  if (isFlattenableSsr(method, obfs, protocol)) {
3661
4000
  ssConstruct(node, {
@@ -3693,7 +4032,7 @@ function explodeSsd(link, nodes) {
3693
4032
  if (data === null || typeof data !== "object" || !("servers" in data)) {
3694
4033
  return;
3695
4034
  }
3696
- const group = String(data.airport ?? "");
4035
+ const group2 = String(data.airport ?? "");
3697
4036
  const servers = data.servers;
3698
4037
  let entries;
3699
4038
  if (Array.isArray(servers)) {
@@ -3719,7 +4058,7 @@ function explodeSsd(link, nodes) {
3719
4058
  const pluginOption = String(entry.plugin_options ?? data.plugin_options ?? "");
3720
4059
  const node = createProxy();
3721
4060
  ssConstruct(node, {
3722
- group,
4061
+ group: group2,
3723
4062
  method,
3724
4063
  password,
3725
4064
  plugin,
@@ -3734,7 +4073,7 @@ function explodeSsd(link, nodes) {
3734
4073
  }
3735
4074
  }
3736
4075
  function explodeSocks(link, node) {
3737
- let group = "";
4076
+ let group2 = "";
3738
4077
  let remark = "";
3739
4078
  let server = "";
3740
4079
  let port = "";
@@ -3773,10 +4112,10 @@ function explodeSocks(link, node) {
3773
4112
  username = urlDecode(getUrlArg(link, "user"));
3774
4113
  password = urlDecode(getUrlArg(link, "pass"));
3775
4114
  remark = urlDecode(getUrlArg(link, "remarks"));
3776
- group = urlDecode(getUrlArg(link, "group"));
4115
+ group2 = urlDecode(getUrlArg(link, "group"));
3777
4116
  }
3778
- if (group === "") {
3779
- group = DEFAULT_GROUPS.socks;
4117
+ if (group2 === "") {
4118
+ group2 = DEFAULT_GROUPS.socks;
3780
4119
  }
3781
4120
  if (remark === "") {
3782
4121
  remark = `${server}:${port}`;
@@ -3784,7 +4123,7 @@ function explodeSocks(link, node) {
3784
4123
  if (port === "0" || port === "") {
3785
4124
  return;
3786
4125
  }
3787
- socksConstruct(node, { group, password, port, remark, server, username });
4126
+ socksConstruct(node, { group: group2, password, port, remark, server, username });
3788
4127
  }
3789
4128
  function explodeHttp(link, node) {
3790
4129
  const server = getUrlArg(link, "server");
@@ -3792,9 +4131,9 @@ function explodeHttp(link, node) {
3792
4131
  const username = urlDecode(getUrlArg(link, "user"));
3793
4132
  const password = urlDecode(getUrlArg(link, "pass"));
3794
4133
  let remark = urlDecode(getUrlArg(link, "remarks"));
3795
- let group = urlDecode(getUrlArg(link, "group"));
3796
- if (group === "") {
3797
- group = DEFAULT_GROUPS.http;
4134
+ let group2 = urlDecode(getUrlArg(link, "group"));
4135
+ if (group2 === "") {
4136
+ group2 = DEFAULT_GROUPS.http;
3798
4137
  }
3799
4138
  if (remark === "") {
3800
4139
  remark = `${server}:${port}`;
@@ -3803,7 +4142,7 @@ function explodeHttp(link, node) {
3803
4142
  return;
3804
4143
  }
3805
4144
  httpConstruct(node, {
3806
- group,
4145
+ group: group2,
3807
4146
  password,
3808
4147
  port,
3809
4148
  remark,
@@ -3815,14 +4154,14 @@ function explodeHttp(link, node) {
3815
4154
  function explodeHttpSub(link, node) {
3816
4155
  let body = link;
3817
4156
  let remarks = "";
3818
- let group = "";
4157
+ let group2 = "";
3819
4158
  const tls = body.includes("https://");
3820
4159
  const qpos = body.indexOf("?");
3821
4160
  if (qpos !== -1) {
3822
4161
  const addition = body.slice(qpos + 1);
3823
4162
  body = body.slice(0, qpos);
3824
4163
  remarks = urlDecode(getUrlArg(addition, "remarks"));
3825
- group = urlDecode(getUrlArg(addition, "group"));
4164
+ group2 = urlDecode(getUrlArg(addition, "group"));
3826
4165
  }
3827
4166
  body = body.slice(body.indexOf("://") + 3);
3828
4167
  body = urlSafeBase64Decode(body);
@@ -3847,8 +4186,8 @@ function explodeHttpSub(link, node) {
3847
4186
  server = match[1];
3848
4187
  port = match[2];
3849
4188
  }
3850
- if (group === "") {
3851
- group = DEFAULT_GROUPS.http;
4189
+ if (group2 === "") {
4190
+ group2 = DEFAULT_GROUPS.http;
3852
4191
  }
3853
4192
  if (remarks === "") {
3854
4193
  remarks = `${server}:${port}`;
@@ -3857,7 +4196,7 @@ function explodeHttpSub(link, node) {
3857
4196
  return;
3858
4197
  }
3859
4198
  httpConstruct(node, {
3860
- group,
4199
+ group: group2,
3861
4200
  password,
3862
4201
  port,
3863
4202
  remark: remarks,
@@ -3898,7 +4237,7 @@ function explodeTrojan(trojan, node) {
3898
4237
  host = getUrlArg(addition, "peer");
3899
4238
  }
3900
4239
  const tfo = getUrlArg(addition, "tfo");
3901
- const group = urlDecode(getUrlArg(addition, "group"));
4240
+ const group2 = urlDecode(getUrlArg(addition, "group"));
3902
4241
  if (getUrlArg(addition, "ws") === "1") {
3903
4242
  path = getUrlArg(addition, "wspath");
3904
4243
  network = "ws";
@@ -3916,7 +4255,7 @@ function explodeTrojan(trojan, node) {
3916
4255
  remark = `${server}:${port}`;
3917
4256
  }
3918
4257
  trojanConstruct(node, {
3919
- group: group === "" ? DEFAULT_GROUPS.trojan : group,
4258
+ group: group2 === "" ? DEFAULT_GROUPS.trojan : group2,
3920
4259
  host,
3921
4260
  network,
3922
4261
  password,
@@ -4085,10 +4424,14 @@ function explodeVless(link, node) {
4085
4424
  const network = getUrlArg(addition, "type");
4086
4425
  const security = getUrlArg(addition, "security");
4087
4426
  const host = getUrlArg(addition, "host");
4088
- const path = urlDecode(getUrlArg(addition, "path"));
4427
+ const rawPath = urlDecode(getUrlArg(addition, "path"));
4428
+ const serviceName = urlDecode(getUrlArg(addition, "serviceName"));
4429
+ const path = rawPath !== "" ? rawPath : serviceName;
4089
4430
  const sni = getUrlArg(addition, "sni");
4090
4431
  const flow = getUrlArg(addition, "flow");
4091
4432
  const fingerprint = getUrlArg(addition, "fp");
4433
+ const publicKey = getUrlArg(addition, "pbk");
4434
+ const shortId = getUrlArg(addition, "sid");
4092
4435
  const alpn = getUrlArg(addition, "alpn");
4093
4436
  if (remark === "") {
4094
4437
  remark = `${server}:${port}`;
@@ -4102,9 +4445,11 @@ function explodeVless(link, node) {
4102
4445
  network,
4103
4446
  path,
4104
4447
  port,
4448
+ publicKey,
4105
4449
  remark,
4106
4450
  scv: getUrlArg(addition, "allowInsecure") === "" ? void 0 : parseTri(getUrlArg(addition, "allowInsecure")),
4107
4451
  server,
4452
+ shortId,
4108
4453
  sni,
4109
4454
  tls: security,
4110
4455
  userId
@@ -4245,6 +4590,425 @@ function parseSubscription(content) {
4245
4590
  // src/generator/clash.ts
4246
4591
  import { stringify as yamlStringify } from "yaml";
4247
4592
 
4593
+ // src/utils/country.ts
4594
+ var REGION_CODES = [
4595
+ "AD",
4596
+ "AE",
4597
+ "AF",
4598
+ "AG",
4599
+ "AI",
4600
+ "AL",
4601
+ "AM",
4602
+ "AO",
4603
+ "AQ",
4604
+ "AR",
4605
+ "AS",
4606
+ "AT",
4607
+ "AU",
4608
+ "AW",
4609
+ "AX",
4610
+ "AZ",
4611
+ "BA",
4612
+ "BB",
4613
+ "BD",
4614
+ "BE",
4615
+ "BF",
4616
+ "BG",
4617
+ "BH",
4618
+ "BI",
4619
+ "BJ",
4620
+ "BL",
4621
+ "BM",
4622
+ "BN",
4623
+ "BO",
4624
+ "BQ",
4625
+ "BR",
4626
+ "BS",
4627
+ "BT",
4628
+ "BV",
4629
+ "BW",
4630
+ "BY",
4631
+ "BZ",
4632
+ "CA",
4633
+ "CC",
4634
+ "CD",
4635
+ "CF",
4636
+ "CG",
4637
+ "CH",
4638
+ "CI",
4639
+ "CK",
4640
+ "CL",
4641
+ "CM",
4642
+ "CN",
4643
+ "CO",
4644
+ "CR",
4645
+ "CU",
4646
+ "CV",
4647
+ "CW",
4648
+ "CX",
4649
+ "CY",
4650
+ "CZ",
4651
+ "DE",
4652
+ "DJ",
4653
+ "DK",
4654
+ "DM",
4655
+ "DO",
4656
+ "DZ",
4657
+ "EC",
4658
+ "EE",
4659
+ "EG",
4660
+ "EH",
4661
+ "ER",
4662
+ "ES",
4663
+ "ET",
4664
+ "FI",
4665
+ "FJ",
4666
+ "FK",
4667
+ "FM",
4668
+ "FO",
4669
+ "FR",
4670
+ "GA",
4671
+ "GB",
4672
+ "GD",
4673
+ "GE",
4674
+ "GF",
4675
+ "GG",
4676
+ "GH",
4677
+ "GI",
4678
+ "GL",
4679
+ "GM",
4680
+ "GN",
4681
+ "GP",
4682
+ "GQ",
4683
+ "GR",
4684
+ "GS",
4685
+ "GT",
4686
+ "GU",
4687
+ "GW",
4688
+ "GY",
4689
+ "HK",
4690
+ "HM",
4691
+ "HN",
4692
+ "HR",
4693
+ "HT",
4694
+ "HU",
4695
+ "ID",
4696
+ "IE",
4697
+ "IL",
4698
+ "IM",
4699
+ "IN",
4700
+ "IO",
4701
+ "IQ",
4702
+ "IR",
4703
+ "IS",
4704
+ "IT",
4705
+ "JE",
4706
+ "JM",
4707
+ "JO",
4708
+ "JP",
4709
+ "KE",
4710
+ "KG",
4711
+ "KH",
4712
+ "KI",
4713
+ "KM",
4714
+ "KN",
4715
+ "KP",
4716
+ "KR",
4717
+ "KW",
4718
+ "KY",
4719
+ "KZ",
4720
+ "LA",
4721
+ "LB",
4722
+ "LC",
4723
+ "LI",
4724
+ "LK",
4725
+ "LR",
4726
+ "LS",
4727
+ "LT",
4728
+ "LU",
4729
+ "LV",
4730
+ "LY",
4731
+ "MA",
4732
+ "MC",
4733
+ "MD",
4734
+ "ME",
4735
+ "MF",
4736
+ "MG",
4737
+ "MH",
4738
+ "MK",
4739
+ "ML",
4740
+ "MM",
4741
+ "MN",
4742
+ "MO",
4743
+ "MP",
4744
+ "MQ",
4745
+ "MR",
4746
+ "MS",
4747
+ "MT",
4748
+ "MU",
4749
+ "MV",
4750
+ "MW",
4751
+ "MX",
4752
+ "MY",
4753
+ "MZ",
4754
+ "NA",
4755
+ "NC",
4756
+ "NE",
4757
+ "NF",
4758
+ "NG",
4759
+ "NI",
4760
+ "NL",
4761
+ "NO",
4762
+ "NP",
4763
+ "NR",
4764
+ "NU",
4765
+ "NZ",
4766
+ "OM",
4767
+ "PA",
4768
+ "PE",
4769
+ "PF",
4770
+ "PG",
4771
+ "PH",
4772
+ "PK",
4773
+ "PL",
4774
+ "PM",
4775
+ "PN",
4776
+ "PR",
4777
+ "PS",
4778
+ "PT",
4779
+ "PW",
4780
+ "PY",
4781
+ "QA",
4782
+ "RE",
4783
+ "RO",
4784
+ "RS",
4785
+ "RU",
4786
+ "RW",
4787
+ "SA",
4788
+ "SB",
4789
+ "SC",
4790
+ "SD",
4791
+ "SE",
4792
+ "SG",
4793
+ "SH",
4794
+ "SI",
4795
+ "SJ",
4796
+ "SK",
4797
+ "SL",
4798
+ "SM",
4799
+ "SN",
4800
+ "SO",
4801
+ "SR",
4802
+ "SS",
4803
+ "ST",
4804
+ "SV",
4805
+ "SX",
4806
+ "SY",
4807
+ "SZ",
4808
+ "TC",
4809
+ "TD",
4810
+ "TF",
4811
+ "TG",
4812
+ "TH",
4813
+ "TJ",
4814
+ "TK",
4815
+ "TL",
4816
+ "TM",
4817
+ "TN",
4818
+ "TO",
4819
+ "TR",
4820
+ "TT",
4821
+ "TV",
4822
+ "TW",
4823
+ "TZ",
4824
+ "UA",
4825
+ "UG",
4826
+ "UM",
4827
+ "US",
4828
+ "UY",
4829
+ "UZ",
4830
+ "VA",
4831
+ "VC",
4832
+ "VE",
4833
+ "VG",
4834
+ "VI",
4835
+ "VN",
4836
+ "VU",
4837
+ "WF",
4838
+ "WS",
4839
+ "YE",
4840
+ "YT",
4841
+ "ZA",
4842
+ "ZM",
4843
+ "ZW"
4844
+ ];
4845
+ var REGION_CODE_SET = new Set(REGION_CODES);
4846
+ var ZH_OVERRIDES = {
4847
+ AE: "\u963F\u8054\u914B",
4848
+ BN: "\u6587\u83B1",
4849
+ BO: "\u73BB\u5229\u7EF4\u4E9A",
4850
+ CD: "\u521A\u679C\uFF08\u91D1\uFF09",
4851
+ CG: "\u521A\u679C\uFF08\u5E03\uFF09",
4852
+ CV: "\u4F5B\u5F97\u89D2",
4853
+ CZ: "\u6377\u514B",
4854
+ DO: "\u591A\u7C73\u5C3C\u52A0",
4855
+ HK: "\u9999\u6E2F",
4856
+ KH: "\u67EC\u57D4\u5BE8",
4857
+ KP: "\u671D\u9C9C",
4858
+ KR: "\u97E9\u56FD",
4859
+ LA: "\u8001\u631D",
4860
+ MD: "\u6469\u5C14\u591A\u74E6",
4861
+ MK: "\u5317\u9A6C\u5176\u987F",
4862
+ MM: "\u7F05\u7538",
4863
+ MO: "\u6FB3\u95E8",
4864
+ NL: "\u8377\u5170",
4865
+ PS: "\u5DF4\u52D2\u65AF\u5766",
4866
+ RU: "\u4FC4\u7F57\u65AF",
4867
+ SY: "\u53D9\u5229\u4E9A",
4868
+ SZ: "\u65AF\u5A01\u58EB\u5170",
4869
+ TL: "\u4E1C\u5E1D\u6C76",
4870
+ TW: "\u53F0\u6E7E",
4871
+ TZ: "\u5766\u6851\u5C3C\u4E9A",
4872
+ VA: "\u68B5\u8482\u5188",
4873
+ VE: "\u59D4\u5185\u745E\u62C9"
4874
+ };
4875
+ var EN_OVERRIDES = {
4876
+ HK: "Hong Kong",
4877
+ MO: "Macau",
4878
+ PS: "Palestine",
4879
+ TW: "Taiwan"
4880
+ };
4881
+ var EXTRA_ALIASES = [
4882
+ "Burma",
4883
+ "Cape Verde",
4884
+ "Czechia",
4885
+ "East Timor",
4886
+ "Holland",
4887
+ "Hong Kong",
4888
+ "Macao",
4889
+ "Macau",
4890
+ "North Korea",
4891
+ "South Korea",
4892
+ "UAE",
4893
+ "UK",
4894
+ "USA",
4895
+ "United Kingdom",
4896
+ "United States"
4897
+ ];
4898
+ function escapeRegExp(value) {
4899
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4900
+ }
4901
+ var displayNameCache = /* @__PURE__ */ new Map();
4902
+ function displayNames(locale) {
4903
+ const cached = displayNameCache.get(locale);
4904
+ if (cached) {
4905
+ return cached;
4906
+ }
4907
+ try {
4908
+ const created = new Intl.DisplayNames([locale], { fallback: "code", type: "region" });
4909
+ displayNameCache.set(locale, created);
4910
+ return created;
4911
+ } catch {
4912
+ return void 0;
4913
+ }
4914
+ }
4915
+ function normalizeRegionCode(code) {
4916
+ const upper = code.trim().toUpperCase();
4917
+ return /^[A-Z]{2}$/.test(upper) ? upper : "";
4918
+ }
4919
+ function regionDisplayName(code, locale = "zh-CN") {
4920
+ const upper = normalizeRegionCode(code);
4921
+ if (upper === "") {
4922
+ return code.trim();
4923
+ }
4924
+ if (locale.toLowerCase().startsWith("zh") && ZH_OVERRIDES[upper] !== void 0) {
4925
+ return ZH_OVERRIDES[upper];
4926
+ }
4927
+ if (locale.toLowerCase().startsWith("en") && EN_OVERRIDES[upper] !== void 0) {
4928
+ return EN_OVERRIDES[upper];
4929
+ }
4930
+ const names = displayNames(locale);
4931
+ return names?.of(upper) ?? upper;
4932
+ }
4933
+ function regionFlag(code) {
4934
+ const upper = normalizeRegionCode(code);
4935
+ if (upper === "") {
4936
+ return "";
4937
+ }
4938
+ const base = 127462;
4939
+ return String.fromCodePoint(base + upper.charCodeAt(0) - 65, base + upper.charCodeAt(1) - 65);
4940
+ }
4941
+ function isRegionCode(code) {
4942
+ return REGION_CODE_SET.has(normalizeRegionCode(code));
4943
+ }
4944
+ var aliasRegex;
4945
+ function buildAliasRegex() {
4946
+ const ascii = [];
4947
+ const other = [];
4948
+ const seen = /* @__PURE__ */ new Set();
4949
+ const add = (alias) => {
4950
+ const trimmed = alias.trim();
4951
+ if (trimmed.length < 2 || seen.has(trimmed)) {
4952
+ return;
4953
+ }
4954
+ seen.add(trimmed);
4955
+ if (/^[\x20-\x7e]+$/.test(trimmed)) {
4956
+ ascii.push(escapeRegExp(trimmed));
4957
+ } else {
4958
+ other.push(escapeRegExp(trimmed));
4959
+ }
4960
+ };
4961
+ for (const code of REGION_CODES) {
4962
+ add(regionDisplayName(code, "zh-CN"));
4963
+ add(regionDisplayName(code, "en"));
4964
+ add(regionFlag(code));
4965
+ }
4966
+ for (const alias of EXTRA_ALIASES) {
4967
+ add(alias);
4968
+ }
4969
+ const parts = [];
4970
+ if (ascii.length > 0) {
4971
+ parts.push(`(?<![A-Za-z])(?:${ascii.join("|")})(?![A-Za-z])`);
4972
+ }
4973
+ if (other.length > 0) {
4974
+ parts.push(`(?:${other.join("|")})`);
4975
+ }
4976
+ return new RegExp(parts.join("|"), "iu");
4977
+ }
4978
+ function getAliasRegex() {
4979
+ aliasRegex ??= buildAliasRegex();
4980
+ return aliasRegex;
4981
+ }
4982
+ function hasRegionName(remark) {
4983
+ const text = remark.trim();
4984
+ if (text === "") {
4985
+ return false;
4986
+ }
4987
+ if (getAliasRegex().test(text)) {
4988
+ return true;
4989
+ }
4990
+ for (const token of text.split(/[^\p{L}\p{N}]+/u)) {
4991
+ if (token === "") {
4992
+ continue;
4993
+ }
4994
+ if (/^\p{Lu}{2}$/u.test(token) && isRegionCode(token)) {
4995
+ return true;
4996
+ }
4997
+ if (/^[A-Za-z]{2}\d{2}/.test(token) && isRegionCode(token.slice(0, 2))) {
4998
+ return true;
4999
+ }
5000
+ }
5001
+ return false;
5002
+ }
5003
+ function formatCountryRemark(remark, code, locale = "zh-CN") {
5004
+ const name = regionDisplayName(code, locale);
5005
+ const trimmed = remark.trim();
5006
+ if (name === "") {
5007
+ return trimmed;
5008
+ }
5009
+ return trimmed === "" ? name : `${name}-${trimmed}`;
5010
+ }
5011
+
4248
5012
  // src/generator/nodes.ts
4249
5013
  function toRegex(pattern) {
4250
5014
  try {
@@ -4453,6 +5217,39 @@ function addEmoji(node, emojiArray) {
4453
5217
  }
4454
5218
  return node.remark;
4455
5219
  }
5220
+ function nodeNameRegex(node, rules) {
5221
+ const original = node.remark;
5222
+ for (const rule of rules) {
5223
+ if (rule.script !== "" || rule.match === "") {
5224
+ continue;
5225
+ }
5226
+ try {
5227
+ node.remark = node.remark.replace(new RegExp(rule.match, "g"), rule.replace);
5228
+ } catch {
5229
+ }
5230
+ }
5231
+ if (node.remark === "") {
5232
+ node.remark = original;
5233
+ }
5234
+ }
5235
+ var NAME_SEPARATOR_EDGES = /^[-_.\s]+|[-_.\s]+$/g;
5236
+ var NAME_SEPARATOR_RUN = /([-_.\s])[-_.\s]+/g;
5237
+ function nodeNameRemove(node, removeList) {
5238
+ const original = node.remark;
5239
+ let remark = node.remark;
5240
+ let removed = false;
5241
+ for (const fragment of removeList) {
5242
+ if (fragment === "" || !remark.includes(fragment)) {
5243
+ continue;
5244
+ }
5245
+ remark = remark.split(fragment).join("");
5246
+ removed = true;
5247
+ }
5248
+ if (removed) {
5249
+ remark = remark.replace(NAME_SEPARATOR_EDGES, "").replace(NAME_SEPARATOR_RUN, "$1");
5250
+ }
5251
+ node.remark = remark === "" ? original : remark;
5252
+ }
4456
5253
  function nodeRename(node, renameArray) {
4457
5254
  const original = node.remark;
4458
5255
  for (const rule of renameArray) {
@@ -4468,15 +5265,39 @@ function nodeRename(node, renameArray) {
4468
5265
  node.remark = original;
4469
5266
  }
4470
5267
  }
5268
+ function addCountryPrefix(node, resolve, locale = "zh-CN") {
5269
+ const remark = node.remark.trim();
5270
+ if (hasRegionName(remark)) {
5271
+ return node.remark;
5272
+ }
5273
+ const code = resolve(node);
5274
+ if (code === void 0 || code === "") {
5275
+ return node.remark;
5276
+ }
5277
+ return formatCountryRemark(remark, code, locale);
5278
+ }
4471
5279
  function preprocessNodes(nodes, options) {
4472
5280
  for (const node of nodes) {
4473
5281
  if (options.removeEmoji) {
4474
5282
  node.remark = removeEmoji(node.remark).trim();
4475
5283
  }
5284
+ if (options.nameRegexArray && options.nameRegexArray.length > 0) {
5285
+ nodeNameRegex(node, options.nameRegexArray);
5286
+ }
5287
+ if (options.nameRemove && options.nameRemove.length > 0) {
5288
+ nodeNameRemove(node, options.nameRemove);
5289
+ }
4476
5290
  nodeRename(node, options.renameArray);
4477
5291
  if (options.addEmoji) {
4478
5292
  node.remark = addEmoji(node, options.emojiArray);
4479
5293
  }
5294
+ if (options.autoCountry && options.countryResolver) {
5295
+ node.remark = addCountryPrefix(
5296
+ node,
5297
+ options.countryResolver,
5298
+ options.countryLocale ?? "zh-CN"
5299
+ );
5300
+ }
4480
5301
  }
4481
5302
  if (options.sortFlag) {
4482
5303
  nodes.sort((a, b) => a.remark < b.remark ? -1 : a.remark > b.remark ? 1 : 0);
@@ -4886,6 +5707,14 @@ function buildClashProxy(x, settings, clashR, remark, skipCertVerify, tfo) {
4886
5707
  if (x.fingerprint !== "") {
4887
5708
  proxy["client-fingerprint"] = x.fingerprint;
4888
5709
  }
5710
+ if (x.publicKey !== "" || x.shortId !== "") {
5711
+ const realityOpts = {};
5712
+ if (x.publicKey !== "") {
5713
+ realityOpts["public-key"] = x.publicKey;
5714
+ }
5715
+ realityOpts["short-id"] = x.shortId;
5716
+ proxy["reality-opts"] = realityOpts;
5717
+ }
4889
5718
  if (x.alpn.length > 0) {
4890
5719
  proxy.alpn = x.alpn;
4891
5720
  }
@@ -4969,60 +5798,60 @@ function buildClash(nodes, extraProxyGroup, settings, clashR = false) {
4969
5798
  return { doc: { proxies }, nodeList };
4970
5799
  }
4971
5800
  const groups = [];
4972
- for (const group of extraProxyGroup) {
5801
+ for (const group2 of extraProxyGroup) {
4973
5802
  const singlegroup = {
4974
- name: group.name,
4975
- type: group.type === "smart" ? "url-test" : group.type
5803
+ name: group2.name,
5804
+ type: group2.type === "smart" ? "url-test" : group2.type
4976
5805
  };
4977
- switch (group.type) {
5806
+ switch (group2.type) {
4978
5807
  case "select":
4979
5808
  case "relay":
4980
5809
  break;
4981
5810
  case "load-balance":
4982
- singlegroup.strategy = group.strategy;
4983
- singlegroup.lazy = group.lazy;
4984
- singlegroup.url = group.url;
4985
- if (group.interval > 0) {
4986
- singlegroup.interval = group.interval;
5811
+ singlegroup.strategy = group2.strategy;
5812
+ singlegroup.lazy = group2.lazy;
5813
+ singlegroup.url = group2.url;
5814
+ if (group2.interval > 0) {
5815
+ singlegroup.interval = group2.interval;
4987
5816
  }
4988
- if (group.tolerance > 0) {
4989
- singlegroup.tolerance = group.tolerance;
5817
+ if (group2.tolerance > 0) {
5818
+ singlegroup.tolerance = group2.tolerance;
4990
5819
  }
4991
5820
  break;
4992
5821
  case "smart":
4993
5822
  case "url-test":
4994
- if (group.lazy !== void 0) {
4995
- singlegroup.lazy = group.lazy;
5823
+ if (group2.lazy !== void 0) {
5824
+ singlegroup.lazy = group2.lazy;
4996
5825
  }
4997
- singlegroup.url = group.url;
4998
- if (group.interval > 0) {
4999
- singlegroup.interval = group.interval;
5826
+ singlegroup.url = group2.url;
5827
+ if (group2.interval > 0) {
5828
+ singlegroup.interval = group2.interval;
5000
5829
  }
5001
- if (group.tolerance > 0) {
5002
- singlegroup.tolerance = group.tolerance;
5830
+ if (group2.tolerance > 0) {
5831
+ singlegroup.tolerance = group2.tolerance;
5003
5832
  }
5004
5833
  break;
5005
5834
  case "fallback":
5006
- singlegroup.url = group.url;
5007
- if (group.interval > 0) {
5008
- singlegroup.interval = group.interval;
5835
+ singlegroup.url = group2.url;
5836
+ if (group2.interval > 0) {
5837
+ singlegroup.interval = group2.interval;
5009
5838
  }
5010
- if (group.tolerance > 0) {
5011
- singlegroup.tolerance = group.tolerance;
5839
+ if (group2.tolerance > 0) {
5840
+ singlegroup.tolerance = group2.tolerance;
5012
5841
  }
5013
5842
  break;
5014
5843
  default:
5015
5844
  continue;
5016
5845
  }
5017
- if (group.disableUdp !== void 0) {
5018
- singlegroup["disable-udp"] = group.disableUdp;
5846
+ if (group2.disableUdp !== void 0) {
5847
+ singlegroup["disable-udp"] = group2.disableUdp;
5019
5848
  }
5020
5849
  const filtered = [];
5021
- for (const rule of group.proxies) {
5850
+ for (const rule of group2.proxies) {
5022
5851
  groupGenerate(rule, nodeList, filtered, true);
5023
5852
  }
5024
- if (group.usingProvider.length > 0) {
5025
- singlegroup.use = group.usingProvider;
5853
+ if (group2.usingProvider.length > 0) {
5854
+ singlegroup.use = group2.usingProvider;
5026
5855
  } else {
5027
5856
  if (filtered.length === 0) {
5028
5857
  filtered.push("DIRECT");
@@ -5056,6 +5885,162 @@ function toClash(nodes, extraProxyGroup, settings, clashR = false) {
5056
5885
  return stringifyClash(doc);
5057
5886
  }
5058
5887
 
5888
+ // src/generator/default-profile.ts
5889
+ var TEST_URL = "http://www.gstatic.com/generate_204";
5890
+ function group(name, type, proxies, extra = {}) {
5891
+ return {
5892
+ disableUdp: void 0,
5893
+ evaluateBeforeUse: void 0,
5894
+ interval: 0,
5895
+ lazy: void 0,
5896
+ name,
5897
+ persistent: void 0,
5898
+ proxies: [...proxies],
5899
+ strategy: "consistent-hashing" /* ConsistentHashing */,
5900
+ timeout: 5,
5901
+ tolerance: 0,
5902
+ type,
5903
+ url: "",
5904
+ usingProvider: [],
5905
+ ...extra
5906
+ };
5907
+ }
5908
+ function getDefaultProxyGroups() {
5909
+ return [
5910
+ group("\u{1F680} \u8282\u70B9\u9009\u62E9", "select" /* Select */, ["[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9", "[]DIRECT", ".*"]),
5911
+ group("\u267B\uFE0F \u81EA\u52A8\u9009\u62E9", "url-test" /* URLTest */, [".*"], {
5912
+ interval: 300,
5913
+ tolerance: 50,
5914
+ url: TEST_URL
5915
+ }),
5916
+ group("\u{1F30D} \u56FD\u5916\u5A92\u4F53", "select" /* Select */, [
5917
+ "[]\u{1F680} \u8282\u70B9\u9009\u62E9",
5918
+ "[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9",
5919
+ "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE",
5920
+ ".*"
5921
+ ]),
5922
+ group("\u{1F4F2} \u7535\u62A5\u4FE1\u606F", "select" /* Select */, ["[]\u{1F680} \u8282\u70B9\u9009\u62E9", "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE", ".*"]),
5923
+ group("\u24C2\uFE0F \u5FAE\u8F6F\u670D\u52A1", "select" /* Select */, ["[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "[]\u{1F680} \u8282\u70B9\u9009\u62E9", ".*"]),
5924
+ group("\u{1F34E} \u82F9\u679C\u670D\u52A1", "select" /* Select */, ["[]\u{1F680} \u8282\u70B9\u9009\u62E9", "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE", ".*"]),
5925
+ group("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "select" /* Select */, ["[]DIRECT", "[]\u{1F680} \u8282\u70B9\u9009\u62E9", "[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9"]),
5926
+ group("\u{1F6D1} \u5168\u7403\u62E6\u622A", "select" /* Select */, ["[]REJECT", "[]DIRECT"]),
5927
+ group("\u{1F343} \u5E94\u7528\u51C0\u5316", "select" /* Select */, ["[]REJECT", "[]DIRECT"]),
5928
+ group("\u{1F41F} \u6F0F\u7F51\u4E4B\u9C7C", "select" /* Select */, [
5929
+ "[]\u{1F680} \u8282\u70B9\u9009\u62E9",
5930
+ "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE",
5931
+ "[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9",
5932
+ ".*"
5933
+ ])
5934
+ ];
5935
+ }
5936
+ function inline(group2, rule) {
5937
+ return {
5938
+ content: `[]${rule}`,
5939
+ ruleGroup: group2,
5940
+ rulePath: "",
5941
+ rulePathTyped: "",
5942
+ ruleType: "surge" /* Surge */,
5943
+ updateInterval: 86400
5944
+ };
5945
+ }
5946
+ function surge(group2, lines) {
5947
+ return {
5948
+ content: lines.join("\n"),
5949
+ ruleGroup: group2,
5950
+ rulePath: "",
5951
+ rulePathTyped: "",
5952
+ ruleType: "surge" /* Surge */,
5953
+ updateInterval: 86400
5954
+ };
5955
+ }
5956
+ function getDefaultRulesets() {
5957
+ return [
5958
+ surge("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", [
5959
+ "DOMAIN-SUFFIX,localhost",
5960
+ "DOMAIN,localhost",
5961
+ "IP-CIDR,127.0.0.0/8",
5962
+ "IP-CIDR,10.0.0.0/8",
5963
+ "IP-CIDR,172.16.0.0/12",
5964
+ "IP-CIDR,192.168.0.0/16",
5965
+ "IP-CIDR,224.0.0.0/4",
5966
+ "DOMAIN-SUFFIX,cn",
5967
+ "DOMAIN-SUFFIX,126.com",
5968
+ "DOMAIN-SUFFIX,163.com",
5969
+ "DOMAIN-SUFFIX,qq.com",
5970
+ "DOMAIN-SUFFIX,baidu.com",
5971
+ "DOMAIN-SUFFIX,alibaba.com",
5972
+ "DOMAIN-SUFFIX,taobao.com"
5973
+ ]),
5974
+ surge("\u{1F6D1} \u5168\u7403\u62E6\u622A", [
5975
+ "DOMAIN-SUFFIX,doubleclick.net",
5976
+ "DOMAIN-SUFFIX,googlesyndication.com",
5977
+ "DOMAIN-SUFFIX,googleadservices.com",
5978
+ "DOMAIN-SUFFIX,ads.yahoo.com",
5979
+ "DOMAIN-KEYWORD,adservice",
5980
+ "DOMAIN-KEYWORD,advertising"
5981
+ ]),
5982
+ surge("\u{1F343} \u5E94\u7528\u51C0\u5316", [
5983
+ "DOMAIN-SUFFIX,app-measurement.com",
5984
+ "DOMAIN-SUFFIX,crashlytics.com",
5985
+ "DOMAIN-KEYWORD,analytics",
5986
+ "DOMAIN-KEYWORD,tracking"
5987
+ ]),
5988
+ surge("\u{1F30D} \u56FD\u5916\u5A92\u4F53", [
5989
+ "DOMAIN-SUFFIX,netflix.com",
5990
+ "DOMAIN-SUFFIX,nflxvideo.net",
5991
+ "DOMAIN-SUFFIX,youtube.com",
5992
+ "DOMAIN-SUFFIX,googlevideo.com",
5993
+ "DOMAIN-SUFFIX,disneyplus.com",
5994
+ "DOMAIN-SUFFIX,spotify.com",
5995
+ "DOMAIN-SUFFIX,tiktok.com"
5996
+ ]),
5997
+ surge("\u{1F4F2} \u7535\u62A5\u4FE1\u606F", [
5998
+ "DOMAIN-SUFFIX,telegram.org",
5999
+ "DOMAIN-SUFFIX,t.me",
6000
+ "DOMAIN-SUFFIX,telegram.me",
6001
+ "IP-CIDR,149.154.160.0/20",
6002
+ "IP-CIDR,91.108.4.0/22"
6003
+ ]),
6004
+ surge("\u24C2\uFE0F \u5FAE\u8F6F\u670D\u52A1", [
6005
+ "DOMAIN-SUFFIX,microsoft.com",
6006
+ "DOMAIN-SUFFIX,windows.com",
6007
+ "DOMAIN-SUFFIX,office.com",
6008
+ "DOMAIN-SUFFIX,live.com"
6009
+ ]),
6010
+ surge("\u{1F34E} \u82F9\u679C\u670D\u52A1", [
6011
+ "DOMAIN-SUFFIX,apple.com",
6012
+ "DOMAIN-SUFFIX,icloud.com",
6013
+ "DOMAIN-SUFFIX,apple-cloudkit.com"
6014
+ ]),
6015
+ surge("\u{1F680} \u8282\u70B9\u9009\u62E9", ["DOMAIN-SUFFIX,google.com", "DOMAIN-SUFFIX,github.com"]),
6016
+ inline("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "GEOIP,CN"),
6017
+ inline("\u{1F41F} \u6F0F\u7F51\u4E4B\u9C7C", "FINAL")
6018
+ ];
6019
+ }
6020
+ function getDefaultRulesetConfigs() {
6021
+ const base = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash";
6022
+ const list = (group2, file) => ({
6023
+ group: group2,
6024
+ interval: 86400,
6025
+ url: `${base}/${file}`
6026
+ });
6027
+ return [
6028
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "LocalAreaNetwork.list"),
6029
+ list("\u{1F6D1} \u5168\u7403\u62E6\u622A", "BanAD.list"),
6030
+ list("\u{1F343} \u5E94\u7528\u51C0\u5316", "BanProgramAD.list"),
6031
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "GoogleCN.list"),
6032
+ list("\u24C2\uFE0F \u5FAE\u8F6F\u670D\u52A1", "Microsoft.list"),
6033
+ list("\u{1F34E} \u82F9\u679C\u670D\u52A1", "Apple.list"),
6034
+ list("\u{1F30D} \u56FD\u5916\u5A92\u4F53", "ProxyMedia.list"),
6035
+ list("\u{1F4F2} \u7535\u62A5\u4FE1\u606F", "Telegram.list"),
6036
+ list("\u{1F680} \u8282\u70B9\u9009\u62E9", "ProxyLite.list"),
6037
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "ChinaDomain.list"),
6038
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "ChinaCompanyIp.list"),
6039
+ { group: "\u{1F3AF} \u5168\u7403\u76F4\u8FDE", interval: 86400, url: "[]GEOIP,CN" },
6040
+ { group: "\u{1F41F} \u6F0F\u7F51\u4E4B\u9C7C", interval: 86400, url: "[]FINAL" }
6041
+ ];
6042
+ }
6043
+
5059
6044
  // src/generator/links.ts
5060
6045
  function vmessLinkConstruct(remarks, add, port, type, id, aid, net, path, host, tls) {
5061
6046
  return JSON.stringify({
@@ -5081,8 +6066,8 @@ function ssLinkConstruct(proxy) {
5081
6066
  link += `#${urlEncode(proxy.remark)}`;
5082
6067
  return link;
5083
6068
  }
5084
- function ssrLinkConstruct(hostname, port, protocol, method, obfs, password, group, remark, obfsParam = "", protocolParam = "") {
5085
- const payload = `${hostname}:${port}:${protocol}:${method}:${obfs}:${urlSafeBase64Encode(password)}/?group=${urlSafeBase64Encode(group)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(obfsParam)}&protoparam=${urlSafeBase64Encode(protocolParam)}`;
6069
+ function ssrLinkConstruct(hostname, port, protocol, method, obfs, password, group2, remark, obfsParam = "", protocolParam = "") {
6070
+ const payload = `${hostname}:${port}:${protocol}:${method}:${obfs}:${urlSafeBase64Encode(password)}/?group=${urlSafeBase64Encode(group2)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(obfsParam)}&protoparam=${urlSafeBase64Encode(protocolParam)}`;
5086
6071
  return `ssr://${urlSafeBase64Encode(payload)}`;
5087
6072
  }
5088
6073
  function trojanLinkConstruct(proxy) {
@@ -5229,8 +6214,8 @@ function proxyToSsSub(nodes, baseConf) {
5229
6214
  }
5230
6215
  return JSON.stringify(proxies);
5231
6216
  }
5232
- function proxyToSsd(nodes, group, userinfo) {
5233
- const resolvedGroup = group === "" ? "SSD" : group;
6217
+ function proxyToSsd(nodes, group2, userinfo) {
6218
+ const resolvedGroup = group2 === "" ? "SSD" : group2;
5234
6219
  const doc = {
5235
6220
  airport: resolvedGroup,
5236
6221
  encryption: "aes-128-gcm",
@@ -5243,7 +6228,7 @@ function proxyToSsd(nodes, group, userinfo) {
5243
6228
  const download = getUrlArg(data, "download");
5244
6229
  const total = getUrlArg(data, "total");
5245
6230
  const expiry = getUrlArg(data, "expire");
5246
- const gb = 1024 ** 3;
6231
+ const gb = 1e3 ** 3;
5247
6232
  doc.traffic_used = (Number(upload || 0) + Number(download || 0)) / gb;
5248
6233
  doc.traffic_total = Number(total || 0) / gb;
5249
6234
  if (expiry !== "") {
@@ -5482,6 +6467,14 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5482
6467
  if (x.host !== "") {
5483
6468
  proxy += `, sni=${x.host}`;
5484
6469
  }
6470
+ if (x.transferProtocol === "ws") {
6471
+ proxy += ", ws=true";
6472
+ if (x.path !== "") {
6473
+ proxy += `, ws-path=${x.path}`;
6474
+ }
6475
+ const wsHost = x.host !== "" ? x.host : x.hostname;
6476
+ proxy += `, ws-headers=Host:"${wsHost}"`;
6477
+ }
5485
6478
  if (scv !== void 0) {
5486
6479
  proxy += `, skip-cert-verify=${triStr(scv)}`;
5487
6480
  }
@@ -5527,7 +6520,7 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5527
6520
  if (surgeVer < 4) {
5528
6521
  continue;
5529
6522
  }
5530
- proxy = `hysteria, ${x.hostname}, ${x.port}, password=${x.password}`;
6523
+ proxy = `hysteria2, ${x.hostname}, ${x.port}, password=${x.password}`;
5531
6524
  if (x.downSpeed) {
5532
6525
  proxy += `, download-bandwidth=${x.downSpeed}`;
5533
6526
  }
@@ -5570,10 +6563,10 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5570
6563
  return { extraSections, groups: [], proxies, sectionOrder };
5571
6564
  }
5572
6565
  const groups = [];
5573
- for (const group of extraProxyGroup) {
6566
+ for (const group2 of extraProxyGroup) {
5574
6567
  const filtered = [];
5575
6568
  let groupLine = "";
5576
- switch (group.type) {
6569
+ switch (group2.type) {
5577
6570
  case "select":
5578
6571
  case "smart":
5579
6572
  case "url-test":
@@ -5585,15 +6578,15 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5585
6578
  }
5586
6579
  break;
5587
6580
  case "ssid": {
5588
- const rest = group.proxies.slice(1).join(",");
5589
- const line = `${group.type},default=${group.proxies[0] ?? ""},${rest}`;
5590
- groups.push(`${group.name} = ${line}`);
6581
+ const rest = group2.proxies.slice(1).join(",");
6582
+ const line = `${group2.type},default=${group2.proxies[0] ?? ""},${rest}`;
6583
+ groups.push(`${group2.name} = ${line}`);
5591
6584
  continue;
5592
6585
  }
5593
6586
  default:
5594
6587
  continue;
5595
6588
  }
5596
- for (const rule of group.proxies) {
6589
+ for (const rule of group2.proxies) {
5597
6590
  groupGenerate(rule, nodelist, filtered, true);
5598
6591
  }
5599
6592
  if (filtered.length === 0) {
@@ -5602,27 +6595,27 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5602
6595
  if (filtered.length === 1) {
5603
6596
  const single = filtered[0].toLowerCase();
5604
6597
  if (single === "direct" || single === "reject" || single === "reject-tinygif") {
5605
- proxies.push(`${group.name} = ${single}`);
6598
+ proxies.push(`${group2.name} = ${single}`);
5606
6599
  continue;
5607
6600
  }
5608
6601
  }
5609
- groupLine = `${group.type},${filtered.join(",")}`;
5610
- if (group.type === "url-test" || group.type === "fallback" || group.type === "load-balance") {
5611
- groupLine += `,url=${group.url},interval=${group.interval}`;
5612
- if (group.tolerance > 0) {
5613
- groupLine += `,tolerance=${group.tolerance}`;
6602
+ groupLine = `${group2.type},${filtered.join(",")}`;
6603
+ if (group2.type === "url-test" || group2.type === "fallback" || group2.type === "load-balance") {
6604
+ groupLine += `,url=${group2.url},interval=${group2.interval}`;
6605
+ if (group2.tolerance > 0) {
6606
+ groupLine += `,tolerance=${group2.tolerance}`;
5614
6607
  }
5615
- if (group.timeout > 0) {
5616
- groupLine += `,timeout=${group.timeout}`;
6608
+ if (group2.timeout > 0) {
6609
+ groupLine += `,timeout=${group2.timeout}`;
5617
6610
  }
5618
- if (group.persistent !== void 0) {
5619
- groupLine += `,persistent=${triStr(group.persistent)}`;
6611
+ if (group2.persistent !== void 0) {
6612
+ groupLine += `,persistent=${triStr(group2.persistent)}`;
5620
6613
  }
5621
- if (group.evaluateBeforeUse !== void 0) {
5622
- groupLine += `,evaluate-before-use=${triStr(group.evaluateBeforeUse)}`;
6614
+ if (group2.evaluateBeforeUse !== void 0) {
6615
+ groupLine += `,evaluate-before-use=${triStr(group2.evaluateBeforeUse)}`;
5623
6616
  }
5624
6617
  }
5625
- groups.push(`${group.name} = ${groupLine}`);
6618
+ groups.push(`${group2.name} = ${groupLine}`);
5626
6619
  }
5627
6620
  return { extraSections, groups, proxies, sectionOrder };
5628
6621
  }
@@ -5722,11 +6715,47 @@ function buildLoonNode(x, settings, remark) {
5722
6715
  proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5723
6716
  }
5724
6717
  break;
6718
+ case "vless" /* VLESS */:
6719
+ if (x.transferProtocol !== "" && x.transferProtocol !== "tcp" && x.transferProtocol !== "ws") {
6720
+ return void 0;
6721
+ }
6722
+ proxy = `VLESS,${hostname},${port},"${x.userId}"`;
6723
+ if (x.flow !== "") {
6724
+ proxy += `,flow=${x.flow}`;
6725
+ }
6726
+ if (x.publicKey !== "") {
6727
+ proxy += `,public-key="${x.publicKey}"`;
6728
+ }
6729
+ if (x.shortId !== "") {
6730
+ proxy += `,short-id=${x.shortId}`;
6731
+ }
6732
+ if (x.transferProtocol === "ws") {
6733
+ proxy += `,path=${x.path},host=${x.host}`;
6734
+ }
6735
+ proxy += `,transport=${x.transferProtocol === "ws" ? "ws" : "tcp"}`;
6736
+ if (x.udp !== void 0) {
6737
+ proxy += `,udp=${x.udp ? "true" : "false"}`;
6738
+ } else {
6739
+ proxy += ",udp=false";
6740
+ }
6741
+ proxy += `,over-tls=${x.tlsSecure ? "true" : "false"}`;
6742
+ if (x.tlsSecure && x.serverName !== "") {
6743
+ proxy += `,sni=${x.serverName}`;
6744
+ }
6745
+ if (scv !== void 0) {
6746
+ proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
6747
+ }
6748
+ break;
5725
6749
  case "trojan" /* Trojan */:
5726
6750
  proxy = `trojan,${hostname},${port},"${x.password}"`;
5727
6751
  if (x.host !== "") {
5728
6752
  proxy += `,tls-name=${x.host}`;
5729
6753
  }
6754
+ if (x.transferProtocol === "ws") {
6755
+ proxy += `,transport=ws,path=${x.path},host=${x.host}`;
6756
+ } else {
6757
+ proxy += ",transport=tcp";
6758
+ }
5730
6759
  if (scv !== void 0) {
5731
6760
  proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5732
6761
  }
@@ -5768,26 +6797,22 @@ function buildLoonNode(x, settings, remark) {
5768
6797
  proxy += `, peers=[{${generatePeer(x, true)}}]`;
5769
6798
  break;
5770
6799
  case "hysteria2" /* Hysteria2 */:
5771
- proxy = `hysteria2,${hostname},${port},"${x.password}"`;
5772
- if (scv !== void 0) {
5773
- proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5774
- }
5775
- if (x.fingerprint !== "") {
5776
- proxy += `,tls-cert-sha256=${x.fingerprint}`;
5777
- }
6800
+ proxy = `Hysteria2,${hostname},${port},"${x.password}"`;
5778
6801
  if (x.sni !== "") {
5779
6802
  proxy += `,sni=${x.sni}`;
5780
6803
  }
5781
- break;
5782
- case "anytls" /* AnyTLS */:
5783
- proxy = `anytls,${hostname},${port},"${x.password}"`;
5784
- if (x.sni !== "") {
5785
- proxy += `,sni=${x.sni}`;
6804
+ if (x.downSpeed > 0) {
6805
+ proxy += `,download-bandwidth=${x.downSpeed}`;
5786
6806
  }
5787
6807
  if (scv !== void 0) {
5788
6808
  proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5789
6809
  }
6810
+ if (x.fingerprint !== "") {
6811
+ proxy += `,tls-cert-sha256=${x.fingerprint}`;
6812
+ }
5790
6813
  break;
6814
+ // Loon has no AnyTLS/TUIC mapping — keep the drop instead of
6815
+ // emitting non-standard lines.
5791
6816
  default:
5792
6817
  return void 0;
5793
6818
  }
@@ -5802,48 +6827,48 @@ function buildLoonNode(x, settings, remark) {
5802
6827
  }
5803
6828
  function buildLoonGroups(extraProxyGroup, nodelist) {
5804
6829
  const lines = [];
5805
- for (const group of extraProxyGroup) {
6830
+ for (const group2 of extraProxyGroup) {
5806
6831
  const filtered = [];
5807
- switch (group.type) {
6832
+ switch (group2.type) {
5808
6833
  case "select":
5809
6834
  case "load-balance":
5810
6835
  case "url-test":
5811
6836
  case "fallback":
5812
6837
  break;
5813
6838
  case "ssid":
5814
- if (group.proxies.length < 2) {
6839
+ if (group2.proxies.length < 2) {
5815
6840
  continue;
5816
6841
  }
5817
6842
  lines.push(
5818
- `${group.name} = ssid,default=${group.proxies[0]},${group.proxies.slice(1).join(",")}`
6843
+ `${group2.name} = ssid,default=${group2.proxies[0]},${group2.proxies.slice(1).join(",")}`
5819
6844
  );
5820
6845
  continue;
5821
6846
  default:
5822
6847
  continue;
5823
6848
  }
5824
- for (const rule of group.proxies) {
6849
+ for (const rule of group2.proxies) {
5825
6850
  groupGenerate(rule, nodelist, filtered, true);
5826
6851
  }
5827
6852
  if (filtered.length === 0) {
5828
6853
  filtered.push("DIRECT");
5829
6854
  }
5830
- let groupLine = `${group.type},${filtered.join(",")}`;
5831
- if (group.type !== "select") {
5832
- groupLine += `,url=${group.url},interval=${group.interval}`;
5833
- if (group.type === "load-balance") {
5834
- groupLine += `,algorithm=${group.strategy === "round-robin" ? "round-robin" : "pcc"}`;
5835
- if (group.timeout > 0) {
5836
- groupLine += `,max-timeout=${group.timeout}`;
6855
+ let groupLine = `${group2.type},${filtered.join(",")}`;
6856
+ if (group2.type !== "select") {
6857
+ groupLine += `,url=${group2.url},interval=${group2.interval}`;
6858
+ if (group2.type === "load-balance") {
6859
+ groupLine += `,algorithm=${group2.strategy === "round-robin" ? "round-robin" : "pcc"}`;
6860
+ if (group2.timeout > 0) {
6861
+ groupLine += `,max-timeout=${group2.timeout}`;
5837
6862
  }
5838
6863
  }
5839
- if (group.type === "url-test" && group.tolerance > 0) {
5840
- groupLine += `,tolerance=${group.tolerance}`;
6864
+ if (group2.type === "url-test" && group2.tolerance > 0) {
6865
+ groupLine += `,tolerance=${group2.tolerance}`;
5841
6866
  }
5842
- if (group.type === "fallback") {
5843
- groupLine += `,max-timeout=${group.timeout}`;
6867
+ if (group2.type === "fallback") {
6868
+ groupLine += `,max-timeout=${group2.timeout}`;
5844
6869
  }
5845
6870
  }
5846
- lines.push(`${group.name} = ${groupLine}`);
6871
+ lines.push(`${group2.name} = ${groupLine}`);
5847
6872
  }
5848
6873
  return lines;
5849
6874
  }
@@ -5874,6 +6899,9 @@ function toLoon(nodes, extraProxyGroup, settings, options) {
5874
6899
  if (groupLines.length > 0) {
5875
6900
  setRawSection(sections, "Proxy Group", groupLines);
5876
6901
  }
6902
+ if (options.rules && options.rules.lines.length > 0) {
6903
+ setRawSection(sections, options.rules.section, options.rules.lines);
6904
+ }
5877
6905
  return renderRawIni(sections);
5878
6906
  }
5879
6907
 
@@ -6004,12 +7032,12 @@ function convertRuleset(content, type) {
6004
7032
  }
6005
7033
  return lines.join("\n");
6006
7034
  }
6007
- function transformRuleToCommon(input, group, noResolveOnly = false) {
7035
+ function transformRuleToCommon(input, group2, noResolveOnly = false) {
6008
7036
  const parts = input.split(",");
6009
7037
  if (parts.length < 2) {
6010
- return `${parts[0] ?? ""},${group}`;
7038
+ return `${parts[0] ?? ""},${group2}`;
6011
7039
  }
6012
- let line = `${parts[0]},${parts[1]},${group}`;
7040
+ let line = `${parts[0]},${parts[1]},${group2}`;
6013
7041
  if (parts.length > 2 && (!noResolveOnly || parts[2] === "no-resolve")) {
6014
7042
  line += `,${parts[2]}`;
6015
7043
  }
@@ -6018,7 +7046,7 @@ function transformRuleToCommon(input, group, noResolveOnly = false) {
6018
7046
  function startsWithAny(line, types) {
6019
7047
  return types.some((type) => line.startsWith(type));
6020
7048
  }
6021
- function collectRuleLines(content, group, noResolveOnly) {
7049
+ function collectRuleLines(content, group2, noResolveOnly) {
6022
7050
  const converted = convertRuleset(content, "surge" /* Surge */);
6023
7051
  const out = [];
6024
7052
  for (const rawLine of converted.split(/\r?\n/)) {
@@ -6032,7 +7060,7 @@ function collectRuleLines(content, group, noResolveOnly) {
6032
7060
  if (line === "") {
6033
7061
  continue;
6034
7062
  }
6035
- out.push(transformRuleToCommon(line, group, noResolveOnly));
7063
+ out.push(transformRuleToCommon(line, group2, noResolveOnly));
6036
7064
  }
6037
7065
  return out;
6038
7066
  }
@@ -6043,13 +7071,13 @@ function rulesetToClashRules(rulesets) {
6043
7071
  if (content === "") {
6044
7072
  continue;
6045
7073
  }
6046
- const group = ruleset.ruleGroup;
7074
+ const group2 = ruleset.ruleGroup;
6047
7075
  if (content.startsWith("[]")) {
6048
7076
  let line = content.slice(2);
6049
7077
  if (line.startsWith("FINAL")) {
6050
7078
  line = `MATCH${line.slice(5)}`;
6051
7079
  }
6052
- rules.push(transformRuleToCommon(line, group));
7080
+ rules.push(transformRuleToCommon(line, group2));
6053
7081
  continue;
6054
7082
  }
6055
7083
  const converted = convertRuleset(content, ruleset.ruleType);
@@ -6064,7 +7092,7 @@ function rulesetToClashRules(rulesets) {
6064
7092
  if (line.includes("//")) {
6065
7093
  line = stripComment(line);
6066
7094
  }
6067
- rules.push(transformRuleToCommon(line, group));
7095
+ rules.push(transformRuleToCommon(line, group2));
6068
7096
  }
6069
7097
  }
6070
7098
  return rules;
@@ -6106,21 +7134,21 @@ function rulesetToSurgeRules(rulesets, surgeVersion) {
6106
7134
  if (ruleset.content === "") {
6107
7135
  continue;
6108
7136
  }
6109
- const group = ruleset.ruleGroup;
7137
+ const group2 = ruleset.ruleGroup;
6110
7138
  if (ruleset.content.startsWith("[]")) {
6111
7139
  let line = ruleset.content.slice(2);
6112
7140
  if (line === "MATCH") {
6113
7141
  line = "FINAL";
6114
7142
  }
6115
7143
  if (surgeVersion === -1 || surgeVersion === -2) {
6116
- line = transformRuleToCommon(line, group, true);
7144
+ line = transformRuleToCommon(line, group2, true);
6117
7145
  } else if (!line.startsWith("AND") && !line.startsWith("OR") && !line.startsWith("NOT")) {
6118
- line = transformRuleToCommon(line, group);
7146
+ line = transformRuleToCommon(line, group2);
6119
7147
  }
6120
7148
  lines.push(line.replace(/,,/g, ","));
6121
7149
  continue;
6122
7150
  }
6123
- for (const rawLine of collectRuleLines(ruleset.content, group, noResolveOnly)) {
7151
+ for (const rawLine of collectRuleLines(ruleset.content, group2, noResolveOnly)) {
6124
7152
  if (surgeVersion === -2 && rawLine.startsWith("IP-CIDR6")) {
6125
7153
  continue;
6126
7154
  }
@@ -6163,11 +7191,11 @@ function rulesetToSingBoxRules(rulesets) {
6163
7191
  if (content === "") {
6164
7192
  continue;
6165
7193
  }
6166
- const group = ruleset.ruleGroup;
7194
+ const group2 = ruleset.ruleGroup;
6167
7195
  if (content.startsWith("[]")) {
6168
7196
  const line = content.slice(2);
6169
7197
  if (line.startsWith("FINAL") || line.startsWith("MATCH")) {
6170
- final = group;
7198
+ final = group2;
6171
7199
  continue;
6172
7200
  }
6173
7201
  const parts = line.split(",");
@@ -6179,7 +7207,7 @@ function rulesetToSingBoxRules(rulesets) {
6179
7207
  if (type === "match" || type === "final") {
6180
7208
  rules.push({ outbound: value });
6181
7209
  } else {
6182
- rules.push({ outbound: group, [type]: value });
7210
+ rules.push({ outbound: group2, [type]: value });
6183
7211
  }
6184
7212
  continue;
6185
7213
  }
@@ -6210,7 +7238,7 @@ function rulesetToSingBoxRules(rulesets) {
6210
7238
  if (Object.keys(rule).length === 0) {
6211
7239
  continue;
6212
7240
  }
6213
- rule.outbound = group;
7241
+ rule.outbound = group2;
6214
7242
  rules.push(rule);
6215
7243
  }
6216
7244
  return { final, rules };
@@ -6271,8 +7299,8 @@ function buildMellowEndpoint(x, remark) {
6271
7299
  }
6272
7300
  function buildMellowGroups(extraProxyGroup, nodelist, remarks) {
6273
7301
  const lines = [];
6274
- for (const group of extraProxyGroup) {
6275
- switch (group.type) {
7302
+ for (const group2 of extraProxyGroup) {
7303
+ switch (group2.type) {
6276
7304
  case "select":
6277
7305
  case "url-test":
6278
7306
  case "fallback":
@@ -6282,7 +7310,7 @@ function buildMellowGroups(extraProxyGroup, nodelist, remarks) {
6282
7310
  continue;
6283
7311
  }
6284
7312
  const filtered = [];
6285
- for (const rule of group.proxies) {
7313
+ for (const rule of group2.proxies) {
6286
7314
  groupGenerate(rule, nodelist, filtered, false);
6287
7315
  }
6288
7316
  if (filtered.length === 0) {
@@ -6292,7 +7320,7 @@ function buildMellowGroups(extraProxyGroup, nodelist, remarks) {
6292
7320
  filtered.push(...remarks);
6293
7321
  }
6294
7322
  }
6295
- lines.push(`${group.name}, ${filtered.join(":")}, latency, interval=300, timeout=6`);
7323
+ lines.push(`${group2.name}, ${filtered.join(":")}, latency, interval=300, timeout=6`);
6296
7324
  }
6297
7325
  return lines;
6298
7326
  }
@@ -6342,7 +7370,7 @@ function toMellow(nodes, extraProxyGroup, settings, options) {
6342
7370
  function buildQuanNode(x, settings, remark, nodeList) {
6343
7371
  const hostname = x.hostname;
6344
7372
  const port = String(x.port);
6345
- const group = x.group;
7373
+ const group2 = x.group;
6346
7374
  let method = x.encryptMethod;
6347
7375
  const scv = x.allowInsecure !== void 0 ? x.allowInsecure : settings.skipCertVerify;
6348
7376
  let proxyStr;
@@ -6351,7 +7379,7 @@ function buildQuanNode(x, settings, remark, nodeList) {
6351
7379
  if (method === "auto") {
6352
7380
  method = "chacha20-ietf-poly1305";
6353
7381
  }
6354
- proxyStr = `${remark} = vmess, ${hostname}, ${port}, ${method}, "${x.userId}", group=${group}`;
7382
+ proxyStr = `${remark} = vmess, ${hostname}, ${port}, ${method}, "${x.userId}", group=${group2}`;
6355
7383
  if (x.tlsSecure) {
6356
7384
  proxyStr += `, over-tls=true, tls-host=${x.host}`;
6357
7385
  if (scv !== void 0) {
@@ -6372,10 +7400,10 @@ function buildQuanNode(x, settings, remark, nodeList) {
6372
7400
  case "ssr" /* ShadowsocksR */:
6373
7401
  if (nodeList) {
6374
7402
  proxyStr = `ssr://${urlSafeBase64Encode(
6375
- `${hostname}:${port}:${x.protocol}:${method}:${x.obfs}:${urlSafeBase64Encode(x.password)}/?group=${urlSafeBase64Encode(group)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(x.obfsParam)}&protoparam=${urlSafeBase64Encode(x.protocolParam)}`
7403
+ `${hostname}:${port}:${x.protocol}:${method}:${x.obfs}:${urlSafeBase64Encode(x.password)}/?group=${urlSafeBase64Encode(group2)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(x.obfsParam)}&protoparam=${urlSafeBase64Encode(x.protocolParam)}`
6376
7404
  )}`;
6377
7405
  } else {
6378
- proxyStr = `${remark} = shadowsocksr, ${hostname}, ${port}, ${method}, "${x.password}", group=${group}, protocol=${x.protocol}, obfs=${x.obfs}`;
7406
+ proxyStr = `${remark} = shadowsocksr, ${hostname}, ${port}, ${method}, "${x.password}", group=${group2}, protocol=${x.protocol}, obfs=${x.obfs}`;
6379
7407
  if (x.protocolParam !== "") {
6380
7408
  proxyStr += `, protocol_param=${x.protocolParam}`;
6381
7409
  }
@@ -6390,9 +7418,9 @@ function buildQuanNode(x, settings, remark, nodeList) {
6390
7418
  if (x.plugin !== "" && x.pluginOption !== "") {
6391
7419
  proxyStr += `/?plugin=${urlEncode(`${x.plugin};${x.pluginOption}`)}`;
6392
7420
  }
6393
- proxyStr += `&group=${urlSafeBase64Encode(group)}#${urlEncode(remark)}`;
7421
+ proxyStr += `&group=${urlSafeBase64Encode(group2)}#${urlEncode(remark)}`;
6394
7422
  } else {
6395
- proxyStr = `${remark} = shadowsocks, ${hostname}, ${port}, ${method}, "${x.password}", group=${group}`;
7423
+ proxyStr = `${remark} = shadowsocks, ${hostname}, ${port}, ${method}, "${x.password}", group=${group2}`;
6396
7424
  if (x.plugin === "obfs-local" && x.pluginOption !== "") {
6397
7425
  proxyStr += `, ${x.pluginOption.replace(/;/g, ", ")}`;
6398
7426
  }
@@ -6400,7 +7428,7 @@ function buildQuanNode(x, settings, remark, nodeList) {
6400
7428
  break;
6401
7429
  case "http" /* HTTP */:
6402
7430
  case "https" /* HTTPS */:
6403
- proxyStr = `${remark} = http, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group}`;
7431
+ proxyStr = `${remark} = http, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group2}`;
6404
7432
  if (x.username !== "" && x.password !== "") {
6405
7433
  proxyStr += `, upstream-proxy-auth=true, upstream-proxy-username=${x.username}, upstream-proxy-password=${x.password}`;
6406
7434
  } else {
@@ -6420,7 +7448,7 @@ function buildQuanNode(x, settings, remark, nodeList) {
6420
7448
  }
6421
7449
  break;
6422
7450
  case "socks5" /* SOCKS5 */:
6423
- proxyStr = `${remark} = socks, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group}`;
7451
+ proxyStr = `${remark} = socks, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group2}`;
6424
7452
  if (x.username !== "" && x.password !== "") {
6425
7453
  proxyStr += `, upstream-proxy-auth=true, upstream-proxy-username=${x.username}, upstream-proxy-password=${x.password}`;
6426
7454
  } else {
@@ -6446,9 +7474,9 @@ function buildQuanNode(x, settings, remark, nodeList) {
6446
7474
  }
6447
7475
  function buildQuanGroups(extraProxyGroup, nodelist) {
6448
7476
  const lines = [];
6449
- for (const group of extraProxyGroup) {
7477
+ for (const group2 of extraProxyGroup) {
6450
7478
  let type;
6451
- switch (group.type) {
7479
+ switch (group2.type) {
6452
7480
  case "select":
6453
7481
  case "fallback":
6454
7482
  type = "static";
@@ -6460,10 +7488,10 @@ function buildQuanGroups(extraProxyGroup, nodelist) {
6460
7488
  type = "balance, round-robin";
6461
7489
  break;
6462
7490
  case "ssid": {
6463
- let singlegroup2 = `${group.name} : wifi = ${group.proxies[0] ?? ""}`;
7491
+ let singlegroup2 = `${group2.name} : wifi = ${group2.proxies[0] ?? ""}`;
6464
7492
  let content = "";
6465
7493
  let celluar = "";
6466
- for (const proxy of group.proxies.slice(1)) {
7494
+ for (const proxy of group2.proxies.slice(1)) {
6467
7495
  const m = /^(.*?),?celluar\s?=\s?(.*?)(,.*)$/.exec(proxy);
6468
7496
  if (!m) {
6469
7497
  content += `${proxy}
@@ -6486,7 +7514,7 @@ ${content.trim().replace(/^,|,$/g, "").replace(/,/g, "\n")}`;
6486
7514
  continue;
6487
7515
  }
6488
7516
  const filtered = [];
6489
- for (const rule of group.proxies) {
7517
+ for (const rule of group2.proxies) {
6490
7518
  groupGenerate(rule, nodelist, filtered, true);
6491
7519
  }
6492
7520
  if (filtered.length === 0) {
@@ -6496,7 +7524,7 @@ ${content.trim().replace(/^,|,$/g, "").replace(/,/g, "\n")}`;
6496
7524
  type = "static";
6497
7525
  }
6498
7526
  const proxies = filtered.join("\n");
6499
- let singlegroup = `${group.name} : ${type}`;
7527
+ let singlegroup = `${group2.name} : ${type}`;
6500
7528
  if (type === "static") {
6501
7529
  singlegroup += `, ${filtered[0]}`;
6502
7530
  }
@@ -6534,6 +7562,9 @@ function toQuan(nodes, extraProxyGroup, settings, options) {
6534
7562
  if (groupLines.length > 0) {
6535
7563
  setRawSection(sections, "POLICY", groupLines);
6536
7564
  }
7565
+ if (options.rules && options.rules.lines.length > 0) {
7566
+ setRawSection(sections, options.rules.section, options.rules.lines);
7567
+ }
6537
7568
  return renderRawIni(sections);
6538
7569
  }
6539
7570
 
@@ -6652,12 +7683,32 @@ function buildQuanxNode(x, settings, remark) {
6652
7683
  }
6653
7684
  }
6654
7685
  break;
6655
- case "anytls" /* AnyTLS */:
6656
- proxyStr = `anytls = ${hostname}:${port}, password=${x.password}`;
6657
- if (x.sni !== "") {
6658
- proxyStr += `, tls-host=${x.sni}`;
7686
+ case "vless" /* VLESS */: {
7687
+ const vmethod = method === "" ? "none" : method;
7688
+ proxyStr = `vless = ${hostname}:${port}, method=${vmethod}, password=${x.userId}`;
7689
+ if (tlsSecure && tls13 !== void 0) {
7690
+ proxyStr += `, tls13=${triStr2(tls13)}`;
7691
+ }
7692
+ const obfsHost = host !== "" ? host : x.serverName !== "" ? x.serverName : x.sni;
7693
+ if (x.transferProtocol === "ws") {
7694
+ proxyStr += tlsSecure ? ", obfs=wss" : ", obfs=ws";
7695
+ proxyStr += `, obfs-host=${obfsHost}, obfs-uri=${path}`;
7696
+ } else if (tlsSecure) {
7697
+ proxyStr += `, obfs=over-tls, obfs-host=${obfsHost}`;
7698
+ }
7699
+ if (x.publicKey !== "") {
7700
+ proxyStr += `, reality-base64-pubkey=${x.publicKey}`;
7701
+ }
7702
+ if (x.shortId !== "") {
7703
+ proxyStr += `, reality-hex-shortid=${x.shortId}`;
7704
+ }
7705
+ if (x.flow !== "") {
7706
+ proxyStr += `, vless-flow=${x.flow}`;
6659
7707
  }
6660
7708
  break;
7709
+ }
7710
+ // Quantumult X has no AnyTLS/Hysteria2/TUIC mapping — drop them instead
7711
+ // of emitting non-standard lines.
6661
7712
  default:
6662
7713
  return void 0;
6663
7714
  }
@@ -6675,10 +7726,10 @@ function buildQuanxNode(x, settings, remark) {
6675
7726
  }
6676
7727
  function buildQuanxGroups(extraProxyGroup, nodelist) {
6677
7728
  const lines = [];
6678
- for (const group of extraProxyGroup) {
7729
+ for (const group2 of extraProxyGroup) {
6679
7730
  let type;
6680
7731
  const filtered = [];
6681
- switch (group.type) {
7732
+ switch (group2.type) {
6682
7733
  case "select":
6683
7734
  type = "static";
6684
7735
  break;
@@ -6693,15 +7744,15 @@ function buildQuanxGroups(extraProxyGroup, nodelist) {
6693
7744
  break;
6694
7745
  case "ssid":
6695
7746
  type = "ssid";
6696
- for (const proxy of group.proxies) {
7747
+ for (const proxy of group2.proxies) {
6697
7748
  filtered.push(proxy.replace(/=/g, ":"));
6698
7749
  }
6699
7750
  break;
6700
7751
  default:
6701
7752
  continue;
6702
7753
  }
6703
- if (group.type !== "ssid") {
6704
- for (const rule of group.proxies) {
7754
+ if (group2.type !== "ssid") {
7755
+ for (const rule of group2.proxies) {
6705
7756
  groupGenerate(rule, nodelist, filtered, true);
6706
7757
  }
6707
7758
  if (filtered.length === 0) {
@@ -6712,11 +7763,11 @@ function buildQuanxGroups(extraProxyGroup, nodelist) {
6712
7763
  }
6713
7764
  }
6714
7765
  const proxies = filtered.join(", ");
6715
- let line = `${type}=${group.name}, ${proxies}`;
6716
- if (group.type !== "select" && group.type !== "ssid") {
6717
- line += `, check-interval=${group.interval}`;
6718
- if (group.tolerance > 0) {
6719
- line += `, tolerance=${group.tolerance}`;
7766
+ let line = `${type}=${group2.name}, ${proxies}`;
7767
+ if (group2.type !== "select" && group2.type !== "ssid") {
7768
+ line += `, check-interval=${group2.interval}`;
7769
+ if (group2.tolerance > 0) {
7770
+ line += `, tolerance=${group2.tolerance}`;
6720
7771
  }
6721
7772
  }
6722
7773
  lines.push(line);
@@ -6750,6 +7801,9 @@ function toQuanX(nodes, extraProxyGroup, settings, options) {
6750
7801
  if (groupLines.length > 0) {
6751
7802
  setRawSection(sections, "policy", groupLines);
6752
7803
  }
7804
+ if (options.rules && options.rules.lines.length > 0) {
7805
+ setRawSection(sections, options.rules.section, options.rules.lines);
7806
+ }
6753
7807
  return renderRawIni(sections);
6754
7808
  }
6755
7809
 
@@ -6845,18 +7899,9 @@ function buildOutbound(x, settings, remark) {
6845
7899
  proxy = out;
6846
7900
  break;
6847
7901
  }
7902
+ // sing-box has no SSR support — drop such nodes.
6848
7903
  case "ssr" /* ShadowsocksR */:
6849
- proxy = {
6850
- ...common,
6851
- method: x.encryptMethod,
6852
- obfs: x.obfs,
6853
- obfs_param: x.obfsParam,
6854
- password: x.password,
6855
- protocol: x.protocol,
6856
- protocol_param: x.protocolParam,
6857
- type: "shadowsocksr"
6858
- };
6859
- break;
7904
+ return void 0;
6860
7905
  case "vmess" /* VMess */: {
6861
7906
  const out = {
6862
7907
  ...common,
@@ -6872,6 +7917,18 @@ function buildOutbound(x, settings, remark) {
6872
7917
  proxy = out;
6873
7918
  break;
6874
7919
  }
7920
+ case "vless" /* VLESS */: {
7921
+ const out = { ...common, type: "vless", uuid: x.userId };
7922
+ if (x.flow !== "") {
7923
+ out.flow = x.flow;
7924
+ }
7925
+ const transport = buildTransport(x);
7926
+ if (transport) {
7927
+ out.transport = transport;
7928
+ }
7929
+ proxy = out;
7930
+ break;
7931
+ }
6875
7932
  case "trojan" /* Trojan */: {
6876
7933
  const out = { ...common, password: x.password, type: "trojan" };
6877
7934
  const transport = buildTransport(x);
@@ -6881,6 +7938,20 @@ function buildOutbound(x, settings, remark) {
6881
7938
  proxy = out;
6882
7939
  break;
6883
7940
  }
7941
+ case "tuic" /* TUIC */: {
7942
+ const out = {
7943
+ ...common,
7944
+ password: x.password,
7945
+ type: "tuic",
7946
+ udp_relay_mode: "native",
7947
+ uuid: x.userId
7948
+ };
7949
+ if (x.congestionControl !== "") {
7950
+ out.congestion_control = x.congestionControl;
7951
+ }
7952
+ proxy = out;
7953
+ break;
7954
+ }
6884
7955
  case "wireguard" /* WireGuard */: {
6885
7956
  const addresses = [x.selfIp];
6886
7957
  if (x.selfIpv6 !== "") {
@@ -6993,6 +8064,17 @@ function buildOutbound(x, settings, remark) {
6993
8064
  }
6994
8065
  const tls = buildTls(x, scv);
6995
8066
  if (tls) {
8067
+ if (x.type === "vless" /* VLESS */ && (x.publicKey !== "" || x.shortId !== "")) {
8068
+ const tlsRec = tls;
8069
+ if (x.fingerprint !== "") {
8070
+ tlsRec.utls = { enabled: true, fingerprint: x.fingerprint };
8071
+ }
8072
+ tlsRec.reality = {
8073
+ enabled: true,
8074
+ public_key: x.publicKey,
8075
+ short_id: x.shortId
8076
+ };
8077
+ }
6996
8078
  proxy.tls = tls;
6997
8079
  }
6998
8080
  if (udp !== void 0 && !udp) {
@@ -7037,9 +8119,9 @@ function toSingBox(nodes, extraProxyGroup, settings, options) {
7037
8119
  remarksList.push(remark);
7038
8120
  }
7039
8121
  if (!options.nodeList) {
7040
- for (const group of extraProxyGroup) {
8122
+ for (const group2 of extraProxyGroup) {
7041
8123
  let type;
7042
- switch (group.type) {
8124
+ switch (group2.type) {
7043
8125
  case "select":
7044
8126
  type = "selector";
7045
8127
  break;
@@ -7052,18 +8134,18 @@ function toSingBox(nodes, extraProxyGroup, settings, options) {
7052
8134
  continue;
7053
8135
  }
7054
8136
  const filtered = [];
7055
- for (const rule of group.proxies) {
8137
+ for (const rule of group2.proxies) {
7056
8138
  groupGenerate(rule, nodelist, filtered, true);
7057
8139
  }
7058
8140
  if (filtered.length === 0) {
7059
8141
  filtered.push("DIRECT");
7060
8142
  }
7061
- const entry = { outbounds: filtered, tag: group.name, type };
7062
- if (group.type === "url-test") {
7063
- entry.url = group.url;
7064
- entry.interval = formatSingBoxInterval(group.interval);
7065
- if (group.tolerance > 0) {
7066
- entry.tolerance = group.tolerance;
8143
+ const entry = { outbounds: filtered, tag: group2.name, type };
8144
+ if (group2.type === "url-test") {
8145
+ entry.url = group2.url;
8146
+ entry.interval = formatSingBoxInterval(group2.interval);
8147
+ if (group2.tolerance > 0) {
8148
+ entry.tolerance = group2.tolerance;
7067
8149
  }
7068
8150
  }
7069
8151
  outbounds.push(entry);
@@ -7083,12 +8165,30 @@ function toSingBox(nodes, extraProxyGroup, settings, options) {
7083
8165
  return JSON.stringify(doc, null, 2);
7084
8166
  }
7085
8167
 
8168
+ // src/generator/surfboard.ts
8169
+ function toSurfboard(nodes, extraProxyGroup, settings, options) {
8170
+ const filtered = nodes.filter(
8171
+ (n) => n.type !== "hysteria2" /* Hysteria2 */ && n.type !== "anytls" /* AnyTLS */
8172
+ );
8173
+ return toSurge(filtered, extraProxyGroup, settings, {
8174
+ baseConf: options.baseConf,
8175
+ nodeList: options.nodeList,
8176
+ rules: options.rules,
8177
+ version: 5
8178
+ });
8179
+ }
8180
+
7086
8181
  // src/engine.ts
7087
8182
  var V2RAY_TYPE_MASK = 1 | 2 | 4 | 8;
7088
8183
  function runPreprocess(nodes, settings) {
7089
8184
  preprocessNodes(nodes, {
7090
8185
  addEmoji: settings.addEmoji,
8186
+ autoCountry: settings.autoCountry,
8187
+ countryLocale: settings.countryLocale,
8188
+ countryResolver: settings.countryResolver,
7091
8189
  emojiArray: settings.emojiArray,
8190
+ nameRegexArray: settings.nameRegexArray,
8191
+ nameRemove: settings.nameRemove,
7092
8192
  removeEmoji: settings.removeEmoji,
7093
8193
  renameArray: settings.renameArray,
7094
8194
  sortFlag: settings.sortFlag
@@ -7100,56 +8200,62 @@ function runPreprocess(nodes, settings) {
7100
8200
  function generate(nodes, options) {
7101
8201
  const settings = { ...createExtraSettings(), ...options.settings };
7102
8202
  runPreprocess(nodes, settings);
8203
+ const groups = resolveGroups(options);
8204
+ const rulesets = resolveRulesets(options, settings);
7103
8205
  switch (options.target) {
7104
8206
  case "clash" /* Clash */:
7105
8207
  case "clashr" /* ClashR */: {
7106
- const { doc } = buildClash(
7107
- nodes,
7108
- options.groups ?? [],
7109
- settings,
7110
- options.target === "clashr" /* ClashR */
7111
- );
7112
- attachClashRules(doc, settings, options.rulesets);
8208
+ const { doc } = buildClash(nodes, groups, settings, options.target === "clashr" /* ClashR */);
8209
+ attachClashRules(doc, settings, rulesets);
7113
8210
  return stringifyClash(doc);
7114
8211
  }
7115
8212
  case "surge" /* Surge */: {
7116
- const surgeVersion = options.surgeVersion ?? 4;
7117
- const rules = options.rulesets ? rulesetToSurgeRules(options.rulesets, surgeVersion) : void 0;
7118
- return toSurge(nodes, options.groups ?? [], settings, {
8213
+ const surgeVersion = options.surgeVersion ?? 5;
8214
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, surgeVersion) : void 0;
8215
+ return toSurge(nodes, groups, settings, {
7119
8216
  baseConf: options.baseConf ?? "",
7120
8217
  nodeList: settings.nodeList,
7121
8218
  rules,
7122
8219
  version: surgeVersion
7123
8220
  });
7124
8221
  }
7125
- case "quanx" /* QuantumultX */:
7126
- return toQuanX(nodes, options.groups ?? [], settings, {
8222
+ case "quanx" /* QuantumultX */: {
8223
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, -1) : void 0;
8224
+ return toQuanX(nodes, groups, settings, {
7127
8225
  baseConf: options.baseConf ?? "",
7128
- nodeList: settings.nodeList
8226
+ nodeList: settings.nodeList,
8227
+ rules
7129
8228
  });
7130
- case "quan" /* Quantumult */:
7131
- return toQuan(nodes, options.groups ?? [], settings, {
8229
+ }
8230
+ case "quan" /* Quantumult */: {
8231
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, -2) : void 0;
8232
+ return toQuan(nodes, groups, settings, {
7132
8233
  baseConf: options.baseConf ?? "",
7133
- nodeList: settings.nodeList
8234
+ nodeList: settings.nodeList,
8235
+ rules
7134
8236
  });
7135
- case "loon" /* Loon */:
7136
- return toLoon(nodes, options.groups ?? [], settings, {
8237
+ }
8238
+ case "loon" /* Loon */: {
8239
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, -4) : void 0;
8240
+ return toLoon(nodes, groups, settings, {
7137
8241
  baseConf: options.baseConf ?? "",
7138
- nodeList: settings.nodeList
8242
+ nodeList: settings.nodeList,
8243
+ rules
7139
8244
  });
8245
+ }
7140
8246
  case "singbox" /* SingBox */:
7141
- return toSingBox(nodes, options.groups ?? [], settings, {
8247
+ return toSingBox(nodes, groups, settings, {
7142
8248
  baseConf: options.baseConf ?? "",
7143
8249
  nodeList: settings.nodeList,
7144
8250
  overwriteOriginalRules: settings.overwriteOriginalRules,
7145
- rulesets: options.rulesets
8251
+ rulesets
7146
8252
  });
7147
8253
  case "mellow" /* Mellow */:
7148
- return toMellow(nodes, options.groups ?? [], settings, {
8254
+ return toMellow(nodes, groups, settings, {
7149
8255
  baseConf: options.baseConf ?? "",
7150
8256
  nodeList: settings.nodeList,
7151
8257
  overwriteOriginalRules: settings.overwriteOriginalRules,
7152
- rulesets: options.rulesets
8258
+ rulesets
7153
8259
  });
7154
8260
  case "v2ray" /* V2Ray */:
7155
8261
  return proxyToSingle(nodes, 4, settings);
@@ -7163,6 +8269,16 @@ function generate(nodes, options) {
7163
8269
  return proxyToSingle(nodes, 2, settings);
7164
8270
  case "ssd" /* SSD */:
7165
8271
  return proxyToSsd(nodes, "", "");
8272
+ case "sssub" /* SSSub */:
8273
+ return proxyToSsSub(nodes, options.baseConf ?? "");
8274
+ case "surfboard" /* Surfboard */: {
8275
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, options.surgeVersion ?? 5) : void 0;
8276
+ return toSurfboard(nodes, groups, settings, {
8277
+ baseConf: options.baseConf ?? "",
8278
+ nodeList: settings.nodeList,
8279
+ rules
8280
+ });
8281
+ }
7166
8282
  case "nodelist" /* NodeList */: {
7167
8283
  settings.nodeList = true;
7168
8284
  return proxyToSingle(nodes, V2RAY_TYPE_MASK, settings);
@@ -7174,7 +8290,28 @@ function generate(nodes, options) {
7174
8290
  function toSip008(nodes, baseConf = "") {
7175
8291
  return proxyToSsSub(nodes, baseConf);
7176
8292
  }
8293
+ function resolveGroups(options) {
8294
+ if (options.includeRules !== false && (!options.groups || options.groups.length === 0)) {
8295
+ return getDefaultProxyGroups();
8296
+ }
8297
+ return options.groups ?? [];
8298
+ }
8299
+ function resolveRulesets(options, settings) {
8300
+ if (options.includeRules === false) {
8301
+ return void 0;
8302
+ }
8303
+ if (!settings.enableRuleGenerator || settings.nodeList) {
8304
+ return void 0;
8305
+ }
8306
+ if (options.rulesets && options.rulesets.length > 0) {
8307
+ return options.rulesets;
8308
+ }
8309
+ return getDefaultRulesets();
8310
+ }
7177
8311
  function attachClashRules(doc, settings, rulesets) {
8312
+ if (!settings.enableRuleGenerator || settings.nodeList) {
8313
+ return;
8314
+ }
7178
8315
  if (!rulesets || rulesets.length === 0) {
7179
8316
  return;
7180
8317
  }
@@ -7205,6 +8342,7 @@ export {
7205
8342
  SURGE2_RULE_TYPES,
7206
8343
  SURGE_RULE_TYPES,
7207
8344
  TargetFormat,
8345
+ addCountryPrefix,
7208
8346
  addEmoji,
7209
8347
  anyTlsConstruct,
7210
8348
  applyMatcher,
@@ -7213,12 +8351,14 @@ export {
7213
8351
  buildClash,
7214
8352
  checkIgnore,
7215
8353
  convertRuleset,
8354
+ convertUserinfo,
7216
8355
  countOccurrences,
7217
8356
  createExtraSettings,
7218
8357
  createProxy,
7219
8358
  createProxyGroupConfig,
7220
8359
  createRequestSettings,
7221
8360
  createRulesetConfig,
8361
+ createSelectGroup,
7222
8362
  dedupeNodes,
7223
8363
  explode,
7224
8364
  explodeAnyTls,
@@ -7251,25 +8391,39 @@ export {
7251
8391
  findSection,
7252
8392
  fnv1a64,
7253
8393
  fnv1a64Hex,
8394
+ formatCountryRemark,
7254
8395
  formatSingBoxInterval,
8396
+ formatSubscriptionUserinfo,
7255
8397
  generate,
7256
8398
  generatePeer,
8399
+ getAllUrlArgs,
8400
+ getDefaultProxyGroups,
8401
+ getDefaultRulesetConfigs,
8402
+ getDefaultRulesets,
7257
8403
  getSectionItems,
7258
8404
  getUrlArg,
7259
8405
  groupGenerate,
8406
+ hasRegionName,
7260
8407
  hasTemplatePlaceholders,
7261
8408
  httpConstruct,
7262
8409
  hysteria2Construct,
7263
8410
  hysteriaConstruct,
8411
+ inferUserinfoProviderFromUrl,
7264
8412
  isBlank,
7265
8413
  isFlattenableSsr,
7266
8414
  isIp,
7267
8415
  isIpv4,
7268
8416
  isIpv6,
7269
8417
  isLink,
8418
+ isRegionCode,
7270
8419
  isSsCipher,
7271
8420
  matchRange,
8421
+ nextMonthlyResetTimestamp,
8422
+ nodeNameRegex,
8423
+ nodeNameRemove,
7272
8424
  nodeRename,
8425
+ normalizeQueryKey,
8426
+ normalizeUserinfoProvider,
7273
8427
  pad2,
7274
8428
  parseClashText,
7275
8429
  parseConfContent,
@@ -7277,6 +8431,7 @@ export {
7277
8431
  parseExternalConfig,
7278
8432
  parseGroupTimes,
7279
8433
  parseIni,
8434
+ parseJmsUserinfo,
7280
8435
  parseLink,
7281
8436
  parsePeers,
7282
8437
  parseProxyGroupType,
@@ -7285,6 +8440,7 @@ export {
7285
8440
  parseRequestSettings,
7286
8441
  parseRulesetConfig,
7287
8442
  parseSubscription,
8443
+ parseSubscriptionUserinfo,
7288
8444
  parseTriBool,
7289
8445
  preprocessNodes,
7290
8446
  processRemark,
@@ -7292,12 +8448,16 @@ export {
7292
8448
  proxyToSsSub,
7293
8449
  proxyToSsd,
7294
8450
  proxyTypeName,
8451
+ queryBoolFlag,
7295
8452
  regMatch,
7296
8453
  regReplace,
8454
+ regionDisplayName,
8455
+ regionFlag,
7297
8456
  removeEmoji,
7298
8457
  renderRawIni,
7299
8458
  renderTemplate,
7300
8459
  replaceAllDistinct,
8460
+ resolveNewFieldName,
7301
8461
  rulesetToClashRules,
7302
8462
  rulesetToSingBoxRules,
7303
8463
  rulesetToSurgeRules,
@@ -7325,6 +8485,7 @@ export {
7325
8485
  toQuanX,
7326
8486
  toSingBox,
7327
8487
  toSip008,
8488
+ toSurfboard,
7328
8489
  toSurge,
7329
8490
  transformRuleToCommon,
7330
8491
  triBoolOr,