@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.cjs CHANGED
@@ -33,6 +33,7 @@ __export(index_exports, {
33
33
  SURGE2_RULE_TYPES: () => SURGE2_RULE_TYPES,
34
34
  SURGE_RULE_TYPES: () => SURGE_RULE_TYPES,
35
35
  TargetFormat: () => TargetFormat,
36
+ addCountryPrefix: () => addCountryPrefix,
36
37
  addEmoji: () => addEmoji,
37
38
  anyTlsConstruct: () => anyTlsConstruct,
38
39
  applyMatcher: () => applyMatcher,
@@ -41,12 +42,14 @@ __export(index_exports, {
41
42
  buildClash: () => buildClash,
42
43
  checkIgnore: () => checkIgnore,
43
44
  convertRuleset: () => convertRuleset,
45
+ convertUserinfo: () => convertUserinfo,
44
46
  countOccurrences: () => countOccurrences,
45
47
  createExtraSettings: () => createExtraSettings,
46
48
  createProxy: () => createProxy,
47
49
  createProxyGroupConfig: () => createProxyGroupConfig,
48
50
  createRequestSettings: () => createRequestSettings,
49
51
  createRulesetConfig: () => createRulesetConfig,
52
+ createSelectGroup: () => createSelectGroup,
50
53
  dedupeNodes: () => dedupeNodes,
51
54
  explode: () => explode,
52
55
  explodeAnyTls: () => explodeAnyTls,
@@ -79,25 +82,39 @@ __export(index_exports, {
79
82
  findSection: () => findSection,
80
83
  fnv1a64: () => fnv1a64,
81
84
  fnv1a64Hex: () => fnv1a64Hex,
85
+ formatCountryRemark: () => formatCountryRemark,
82
86
  formatSingBoxInterval: () => formatSingBoxInterval,
87
+ formatSubscriptionUserinfo: () => formatSubscriptionUserinfo,
83
88
  generate: () => generate,
84
89
  generatePeer: () => generatePeer,
90
+ getAllUrlArgs: () => getAllUrlArgs,
91
+ getDefaultProxyGroups: () => getDefaultProxyGroups,
92
+ getDefaultRulesetConfigs: () => getDefaultRulesetConfigs,
93
+ getDefaultRulesets: () => getDefaultRulesets,
85
94
  getSectionItems: () => getSectionItems,
86
95
  getUrlArg: () => getUrlArg,
87
96
  groupGenerate: () => groupGenerate,
97
+ hasRegionName: () => hasRegionName,
88
98
  hasTemplatePlaceholders: () => hasTemplatePlaceholders,
89
99
  httpConstruct: () => httpConstruct,
90
100
  hysteria2Construct: () => hysteria2Construct,
91
101
  hysteriaConstruct: () => hysteriaConstruct,
102
+ inferUserinfoProviderFromUrl: () => inferUserinfoProviderFromUrl,
92
103
  isBlank: () => isBlank,
93
104
  isFlattenableSsr: () => isFlattenableSsr,
94
105
  isIp: () => isIp,
95
106
  isIpv4: () => isIpv4,
96
107
  isIpv6: () => isIpv6,
97
108
  isLink: () => isLink,
109
+ isRegionCode: () => isRegionCode,
98
110
  isSsCipher: () => isSsCipher,
99
111
  matchRange: () => matchRange,
112
+ nextMonthlyResetTimestamp: () => nextMonthlyResetTimestamp,
113
+ nodeNameRegex: () => nodeNameRegex,
114
+ nodeNameRemove: () => nodeNameRemove,
100
115
  nodeRename: () => nodeRename,
116
+ normalizeQueryKey: () => normalizeQueryKey,
117
+ normalizeUserinfoProvider: () => normalizeUserinfoProvider,
101
118
  pad2: () => pad2,
102
119
  parseClashText: () => parseClashText,
103
120
  parseConfContent: () => parseConfContent,
@@ -105,6 +122,7 @@ __export(index_exports, {
105
122
  parseExternalConfig: () => parseExternalConfig,
106
123
  parseGroupTimes: () => parseGroupTimes,
107
124
  parseIni: () => parseIni,
125
+ parseJmsUserinfo: () => parseJmsUserinfo,
108
126
  parseLink: () => parseLink,
109
127
  parsePeers: () => parsePeers,
110
128
  parseProxyGroupType: () => parseProxyGroupType,
@@ -113,6 +131,7 @@ __export(index_exports, {
113
131
  parseRequestSettings: () => parseRequestSettings,
114
132
  parseRulesetConfig: () => parseRulesetConfig,
115
133
  parseSubscription: () => parseSubscription,
134
+ parseSubscriptionUserinfo: () => parseSubscriptionUserinfo,
116
135
  parseTriBool: () => parseTriBool,
117
136
  preprocessNodes: () => preprocessNodes,
118
137
  processRemark: () => processRemark,
@@ -120,12 +139,16 @@ __export(index_exports, {
120
139
  proxyToSsSub: () => proxyToSsSub,
121
140
  proxyToSsd: () => proxyToSsd,
122
141
  proxyTypeName: () => proxyTypeName,
142
+ queryBoolFlag: () => queryBoolFlag,
123
143
  regMatch: () => regMatch,
124
144
  regReplace: () => regReplace,
145
+ regionDisplayName: () => regionDisplayName,
146
+ regionFlag: () => regionFlag,
125
147
  removeEmoji: () => removeEmoji,
126
148
  renderRawIni: () => renderRawIni,
127
149
  renderTemplate: () => renderTemplate,
128
150
  replaceAllDistinct: () => replaceAllDistinct,
151
+ resolveNewFieldName: () => resolveNewFieldName,
129
152
  rulesetToClashRules: () => rulesetToClashRules,
130
153
  rulesetToSingBoxRules: () => rulesetToSingBoxRules,
131
154
  rulesetToSurgeRules: () => rulesetToSurgeRules,
@@ -153,6 +176,7 @@ __export(index_exports, {
153
176
  toQuanX: () => toQuanX,
154
177
  toSingBox: () => toSingBox,
155
178
  toSip008: () => toSip008,
179
+ toSurfboard: () => toSurfboard,
156
180
  toSurge: () => toSurge,
157
181
  transformRuleToCommon: () => transformRuleToCommon,
158
182
  triBoolOr: () => triBoolOr,
@@ -208,6 +232,23 @@ function createProxyGroupConfig() {
208
232
  usingProvider: []
209
233
  };
210
234
  }
235
+ function createSelectGroup(name) {
236
+ return {
237
+ disableUdp: void 0,
238
+ evaluateBeforeUse: void 0,
239
+ interval: 0,
240
+ lazy: void 0,
241
+ name,
242
+ persistent: void 0,
243
+ proxies: ["[]DIRECT", "!!TYPE=SS", "!!TYPE=SSR", "!!TYPE=VMESS", "!!TYPE=TROJAN"],
244
+ strategy: "consistent-hashing" /* ConsistentHashing */,
245
+ timeout: 0,
246
+ tolerance: 0,
247
+ type: "select" /* Select */,
248
+ url: "",
249
+ usingProvider: []
250
+ };
251
+ }
211
252
  function parseProxyGroupType(value) {
212
253
  const normalized = value.trim().toLowerCase();
213
254
  switch (normalized) {
@@ -347,6 +388,7 @@ function createProxy() {
347
388
  selfIp: "",
348
389
  selfIpv6: "",
349
390
  serverName: "",
391
+ shortId: "",
350
392
  snellVersion: 0,
351
393
  sni: "",
352
394
  tcpFastOpen: void 0,
@@ -382,6 +424,7 @@ var TargetFormat = /* @__PURE__ */ ((TargetFormat2) => {
382
424
  TargetFormat2["Clash"] = "clash";
383
425
  TargetFormat2["ClashR"] = "clashr";
384
426
  TargetFormat2["Surge"] = "surge";
427
+ TargetFormat2["Surfboard"] = "surfboard";
385
428
  TargetFormat2["QuantumultX"] = "quanx";
386
429
  TargetFormat2["Quantumult"] = "quan";
387
430
  TargetFormat2["Loon"] = "loon";
@@ -390,6 +433,7 @@ var TargetFormat = /* @__PURE__ */ ((TargetFormat2) => {
390
433
  TargetFormat2["Trojan"] = "trojan";
391
434
  TargetFormat2["SSD"] = "ssd";
392
435
  TargetFormat2["SSC"] = "ss";
436
+ TargetFormat2["SSSub"] = "sssub";
393
437
  TargetFormat2["SSR"] = "ssr";
394
438
  TargetFormat2["SingBox"] = "singbox";
395
439
  TargetFormat2["Mixed"] = "mixed";
@@ -402,16 +446,21 @@ function createExtraSettings() {
402
446
  addEmoji: false,
403
447
  appendProxyType: false,
404
448
  authorized: false,
449
+ autoCountry: false,
405
450
  clashClassicalRuleset: false,
406
- clashNewFieldName: false,
451
+ clashNewFieldName: true,
407
452
  clashProxiesStyle: "flow",
408
453
  clashProxyGroupsStyle: "flow",
409
454
  clashScript: false,
455
+ countryLocale: "zh-CN",
456
+ countryResolver: void 0,
410
457
  dedupe: false,
411
458
  emojiArray: [],
412
459
  enableRuleGenerator: true,
413
460
  filterDeprecated: false,
414
461
  managedConfigPrefix: "",
462
+ nameRegexArray: [],
463
+ nameRemove: [],
415
464
  nodeList: false,
416
465
  overwriteOriginalRules: true,
417
466
  quanxDevId: "",
@@ -717,27 +766,35 @@ function urlEncode(input) {
717
766
  (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`
718
767
  );
719
768
  }
720
- function getUrlArg(query, key) {
769
+ function normalizeQueryKey(key) {
770
+ return key.toLowerCase().replace(/_/g, "-");
771
+ }
772
+ function getAllUrlArgs(query, key) {
721
773
  if (query === "") {
722
- return "";
723
- }
724
- const normalized = query.startsWith("?") ? query.slice(1) : query;
725
- const parts = normalized.split("&");
726
- let found = "";
727
- const target = key.toLowerCase();
728
- for (const part of parts) {
774
+ return [];
775
+ }
776
+ const hashCut = query.split("#", 1)[0];
777
+ const qIndex = hashCut.indexOf("?");
778
+ const rawQuery = qIndex >= 0 ? hashCut.slice(qIndex + 1) : hashCut;
779
+ const normalized = rawQuery.startsWith("?") ? rawQuery.slice(1) : rawQuery;
780
+ const values = [];
781
+ const target = normalizeQueryKey(key);
782
+ for (const part of normalized.split("&")) {
729
783
  if (part === "") {
730
784
  continue;
731
785
  }
732
786
  const eq = part.indexOf("=");
733
787
  const rawKey = eq >= 0 ? part.slice(0, eq) : part;
734
- if (rawKey.toLowerCase() !== target) {
788
+ if (normalizeQueryKey(urlDecode(rawKey)) !== target) {
735
789
  continue;
736
790
  }
737
- const rawValue = eq >= 0 ? part.slice(eq + 1) : "";
738
- found = urlDecode(rawValue);
791
+ values.push(urlDecode(eq >= 0 ? part.slice(eq + 1) : ""));
739
792
  }
740
- return found.trim();
793
+ return values;
794
+ }
795
+ function getUrlArg(query, key) {
796
+ const values = getAllUrlArgs(query, key);
797
+ return values.length > 0 ? values[values.length - 1].trim() : "";
741
798
  }
742
799
  function splitUrl(raw) {
743
800
  const hashIndex = raw.indexOf("#");
@@ -813,7 +870,7 @@ function parseCustomProxyGroup(value) {
813
870
  if (type === void 0) {
814
871
  return void 0;
815
872
  }
816
- const group = {
873
+ const group2 = {
817
874
  disableUdp: void 0,
818
875
  evaluateBeforeUse: void 0,
819
876
  interval: 0,
@@ -834,29 +891,29 @@ function parseCustomProxyGroup(value) {
834
891
  return void 0;
835
892
  }
836
893
  upperBound -= 2;
837
- group.url = parts[upperBound];
894
+ group2.url = parts[upperBound];
838
895
  const times = parseGroupTimes(parts[upperBound + 1]);
839
- group.interval = times.interval;
840
- group.timeout = times.timeout;
841
- group.tolerance = times.tolerance;
896
+ group2.interval = times.interval;
897
+ group2.timeout = times.timeout;
898
+ group2.tolerance = times.tolerance;
842
899
  }
843
900
  for (let i = 2; i < upperBound; i += 1) {
844
901
  const rule = parts[i];
845
902
  if (startsWith(rule, "!!PROVIDER=")) {
846
- group.usingProvider.push(...rule.slice(11).split(","));
903
+ group2.usingProvider.push(...rule.slice(11).split(","));
847
904
  } else {
848
- group.proxies.push(rule);
905
+ group2.proxies.push(rule);
849
906
  }
850
907
  }
851
- return group;
908
+ return group2;
852
909
  }
853
910
  function parseRulesetConfig(value) {
854
911
  const pos = value.indexOf(",");
855
912
  if (pos === -1) {
856
913
  return void 0;
857
914
  }
858
- const group = value.slice(0, pos);
859
- const config = { group, interval: 86400, url: "" };
915
+ const group2 = value.slice(0, pos);
916
+ const config = { group: group2, interval: 86400, url: "" };
860
917
  if (value.slice(pos + 1, pos + 3) === "[]") {
861
918
  config.url = value.slice(pos + 1);
862
919
  return config;
@@ -882,12 +939,15 @@ function parseRegexMatch(value, delimiter) {
882
939
  function parseExternalConfig(content) {
883
940
  const result = {
884
941
  addEmoji: false,
942
+ autoCountry: false,
885
943
  baseFiles: {},
886
944
  emoji: [],
887
945
  enableRuleGenerator: true,
888
946
  excludeRemarks: [],
889
947
  groups: [],
890
948
  includeRemarks: [],
949
+ nameRegex: [],
950
+ nameRemove: [],
891
951
  overwriteOriginalRules: true,
892
952
  removeEmoji: false,
893
953
  rename: [],
@@ -932,6 +992,17 @@ function parseExternalConfig(content) {
932
992
  case "rename_node":
933
993
  result.rename.push(parseRegexMatch(value, "@"));
934
994
  break;
995
+ case "name_regex":
996
+ result.nameRegex.push(parseRegexMatch(value, "@"));
997
+ break;
998
+ case "name_remove":
999
+ for (const fragment of value.split(",")) {
1000
+ const trimmed = fragment.trim();
1001
+ if (trimmed !== "") {
1002
+ result.nameRemove.push(trimmed);
1003
+ }
1004
+ }
1005
+ break;
935
1006
  case "emoji":
936
1007
  case "emojis":
937
1008
  result.emoji.push(parseRegexMatch(value, ","));
@@ -951,6 +1022,10 @@ function parseExternalConfig(content) {
951
1022
  case "add_emoji":
952
1023
  result.addEmoji = value.toLowerCase() === "true";
953
1024
  break;
1025
+ case "auto_country":
1026
+ case "country":
1027
+ result.autoCountry = value.toLowerCase() === "true";
1028
+ break;
954
1029
  case "remove_old_emoji":
955
1030
  case "remove_emoji":
956
1031
  result.removeEmoji = value.toLowerCase() === "true";
@@ -974,7 +1049,13 @@ function parseExternalConfig(content) {
974
1049
 
975
1050
  // src/settings/request-params.ts
976
1051
  var TARGETS = new Set(Object.values(TargetFormat));
1052
+ function hasArg(query, key) {
1053
+ return getAllUrlArgs(query, key).length > 0;
1054
+ }
977
1055
  function boolArg(query, key) {
1056
+ if (!hasArg(query, key)) {
1057
+ return false;
1058
+ }
978
1059
  const value = getUrlArg(query, key).toLowerCase();
979
1060
  return value === "true" || value === "1" || value === "";
980
1061
  }
@@ -986,13 +1067,47 @@ function listArg(query, key) {
986
1067
  const value = getUrlArg(query, key);
987
1068
  return value === "" ? [] : value.split(",");
988
1069
  }
1070
+ function resolveNewFieldName(query) {
1071
+ if (boolArg(query, "legacy") || boolArg(query, "legacy_field_name") || boolArg(query, "legacy-field-name")) {
1072
+ return false;
1073
+ }
1074
+ if (hasArg(query, "new_name") || hasArg(query, "new_field_name") || hasArg(query, "new-name") || hasArg(query, "new-field-name")) {
1075
+ return boolArg(query, "new_name") || boolArg(query, "new_field_name") || boolArg(query, "new-name") || boolArg(query, "new-field-name");
1076
+ }
1077
+ return true;
1078
+ }
1079
+ function firstArg(query, ...keys) {
1080
+ for (const key of keys) {
1081
+ const value = getUrlArg(query, key);
1082
+ if (value !== "") {
1083
+ return value;
1084
+ }
1085
+ }
1086
+ return "";
1087
+ }
1088
+ function queryBoolFlag(query, ...keys) {
1089
+ let found = false;
1090
+ let result = false;
1091
+ for (const key of keys) {
1092
+ for (const raw of getAllUrlArgs(query, key)) {
1093
+ found = true;
1094
+ const normalized = raw.trim().toLowerCase();
1095
+ if (normalized === "" || normalized === "true" || normalized === "1") {
1096
+ result = true;
1097
+ }
1098
+ }
1099
+ }
1100
+ return found ? result : void 0;
1101
+ }
989
1102
  function createRequestSettings() {
990
1103
  return {
991
1104
  addEmoji: false,
992
1105
  appendType: false,
1106
+ autoCountry: false,
993
1107
  autoExtract: false,
994
1108
  clashScript: false,
995
1109
  classicalRuleset: false,
1110
+ dedupe: false,
996
1111
  emoji: [],
997
1112
  excludeRemarks: [],
998
1113
  filterDeprecated: false,
@@ -1002,7 +1117,9 @@ function createRequestSettings() {
1002
1117
  interval: 0,
1003
1118
  list: false,
1004
1119
  mixin: "",
1005
- newFieldName: false,
1120
+ nameRegex: [],
1121
+ nameRemove: [],
1122
+ newFieldName: true,
1006
1123
  nodeList: false,
1007
1124
  proxiesStyle: "flow",
1008
1125
  proxyGroupsStyle: "flow",
@@ -1017,7 +1134,9 @@ function createRequestSettings() {
1017
1134
  tls13: void 0,
1018
1135
  udp: void 0,
1019
1136
  uploadPath: "",
1020
- url: ""
1137
+ url: "",
1138
+ userinfoProvider: "",
1139
+ userinfoUrl: ""
1021
1140
  };
1022
1141
  }
1023
1142
  function parseRequestSettings(query) {
@@ -1029,11 +1148,14 @@ function parseRequestSettings(query) {
1029
1148
  settings.url = getUrlArg(query, "url");
1030
1149
  settings.list = boolArg(query, "list");
1031
1150
  settings.nodeList = settings.list;
1151
+ settings.sort = queryBoolFlag(query, "sort", "sort_flag", "sort-flag") ?? false;
1152
+ settings.dedupe = queryBoolFlag(query, "dedupe", "deduplicate") ?? false;
1032
1153
  settings.addEmoji = boolArg(query, "add_emoji");
1033
1154
  settings.removeEmoji = boolArg(query, "remove_emoji");
1155
+ settings.autoCountry = boolArg(query, "auto_country");
1034
1156
  settings.appendType = boolArg(query, "append_type");
1035
1157
  settings.filterDeprecated = boolArg(query, "filter_deprecated");
1036
- settings.newFieldName = boolArg(query, "new_name") || boolArg(query, "new_field_name");
1158
+ settings.newFieldName = resolveNewFieldName(query);
1037
1159
  settings.classicalRuleset = boolArg(query, "classical");
1038
1160
  settings.insert = getUrlArg(query, "insert") !== "";
1039
1161
  settings.clashScript = boolArg(query, "clash_script");
@@ -1050,10 +1172,20 @@ function parseRequestSettings(query) {
1050
1172
  settings.proxyGroupsStyle = getUrlArg(query, "proxy_groups_style") || "flow";
1051
1173
  settings.includeRemarks = listArg(query, "include");
1052
1174
  settings.excludeRemarks = listArg(query, "exclude");
1175
+ settings.userinfoUrl = firstArg(query, "userinfo_url", "userinfo-url", "userinfo");
1176
+ settings.userinfoProvider = firstArg(
1177
+ query,
1178
+ "userinfo_provider",
1179
+ "userinfo-provider",
1180
+ "userinfo_type",
1181
+ "userinfo-type"
1182
+ );
1053
1183
  const renameRaw = getUrlArg(query, "rename");
1054
1184
  if (renameRaw !== "") {
1055
1185
  settings.rename = [parseRegexMatch(renameRaw, "@")];
1056
1186
  }
1187
+ settings.nameRegex = getAllUrlArgs(query, "name_regex").flatMap((raw) => raw.split("\n")).map((raw) => raw.trim()).filter((raw) => raw !== "").map((raw) => parseRegexMatch(raw, "@"));
1188
+ settings.nameRemove = getAllUrlArgs(query, "name_remove").flatMap((raw) => raw.split(",")).map((raw) => raw.trim()).filter((raw) => raw !== "");
1057
1189
  const emojiRaw = getUrlArg(query, "emoji");
1058
1190
  if (emojiRaw !== "") {
1059
1191
  settings.emoji = [parseRegexMatch(emojiRaw, ",")];
@@ -1061,6 +1193,235 @@ function parseRequestSettings(query) {
1061
1193
  return settings;
1062
1194
  }
1063
1195
 
1196
+ // src/userinfo.ts
1197
+ var PROVIDER_ALIASES = {
1198
+ auto: "auto",
1199
+ clash: "subscription",
1200
+ jms: "jms",
1201
+ "just-my-socks": "jms",
1202
+ justmysocks: "jms",
1203
+ raw: "subscription",
1204
+ sub: "subscription",
1205
+ subscription: "subscription",
1206
+ "subscription-userinfo": "subscription"
1207
+ };
1208
+ function normalizeUserinfoProvider(provider) {
1209
+ if (!provider) {
1210
+ return "auto";
1211
+ }
1212
+ const key = provider.trim().toLowerCase().replace(/_/g, "-");
1213
+ return PROVIDER_ALIASES[key] ?? "auto";
1214
+ }
1215
+ function inferUserinfoProviderFromUrl(url) {
1216
+ const squished = url.trim().toLowerCase().replace(/[-_]/g, "");
1217
+ if (squished.includes("justmysocks") || squished.includes("jmssub") || squished.includes("getbwcounter")) {
1218
+ return "jms";
1219
+ }
1220
+ return void 0;
1221
+ }
1222
+ function toNonNegativeNumber(value) {
1223
+ if (typeof value === "number") {
1224
+ return Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;
1225
+ }
1226
+ if (typeof value === "string") {
1227
+ const parsed = toInt(value);
1228
+ return parsed > 0 ? parsed : 0;
1229
+ }
1230
+ return 0;
1231
+ }
1232
+ function daysInUtcMonth(year, month) {
1233
+ return new Date(Date.UTC(year, month + 1, 0)).getUTCDate();
1234
+ }
1235
+ var DECIMAL_TO_BINARY = 1024 ** 3 / 1e3 ** 3;
1236
+ function scaleDecimalBytes(value) {
1237
+ return Math.round(value * DECIMAL_TO_BINARY);
1238
+ }
1239
+ function scaleUserinfoDecimal(data) {
1240
+ return {
1241
+ download: scaleDecimalBytes(data.download),
1242
+ expire: data.expire,
1243
+ total: scaleDecimalBytes(data.total),
1244
+ upload: scaleDecimalBytes(data.upload)
1245
+ };
1246
+ }
1247
+ function nextMonthlyResetTimestamp(resetDay, nowSec) {
1248
+ const day = Math.trunc(resetDay);
1249
+ if (!Number.isFinite(day) || day < 1 || day > 31) {
1250
+ return 0;
1251
+ }
1252
+ const now = nowSec && nowSec > 0 ? Math.trunc(nowSec) : Math.trunc(Date.now() / 1e3);
1253
+ const current = new Date(now * 1e3);
1254
+ const year = current.getUTCFullYear();
1255
+ const month = current.getUTCMonth();
1256
+ const today = current.getUTCDate();
1257
+ const useCurrentMonth = today < day;
1258
+ const targetYear = useCurrentMonth ? year : month === 11 ? year + 1 : year;
1259
+ const targetMonth = useCurrentMonth ? month : (month + 1) % 12;
1260
+ const clampedDay = Math.min(day, daysInUtcMonth(targetYear, targetMonth));
1261
+ return Math.trunc(Date.UTC(targetYear, targetMonth, clampedDay) / 1e3);
1262
+ }
1263
+ function parseJmsUserinfo(raw, nowSec) {
1264
+ let parsed;
1265
+ try {
1266
+ parsed = JSON.parse(raw);
1267
+ } catch {
1268
+ return void 0;
1269
+ }
1270
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1271
+ return void 0;
1272
+ }
1273
+ const record = parsed;
1274
+ 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;
1275
+ if (!hasJmsKeys) {
1276
+ return void 0;
1277
+ }
1278
+ const limitRaw = record["monthly_bw_limit_b"] ?? record["monthlyBwLimitB"];
1279
+ const usedRaw = record["bw_counter_b"] ?? record["bwCounterB"];
1280
+ const resetRaw = record["bw_reset_day_of_month"] ?? record["bwResetDayOfMonth"] ?? record["reset_day"] ?? record["resetDay"];
1281
+ const payload = {
1282
+ bwCounterB: usedRaw,
1283
+ bwResetDayOfMonth: resetRaw,
1284
+ monthlyBwLimitB: limitRaw
1285
+ };
1286
+ const total = scaleDecimalBytes(toNonNegativeNumber(payload.monthlyBwLimitB));
1287
+ const download = scaleDecimalBytes(toNonNegativeNumber(payload.bwCounterB));
1288
+ const resetDay = toInt(
1289
+ typeof payload.bwResetDayOfMonth === "number" ? String(payload.bwResetDayOfMonth) : payload.bwResetDayOfMonth ?? ""
1290
+ );
1291
+ return {
1292
+ download,
1293
+ expire: resetDay > 0 ? nextMonthlyResetTimestamp(resetDay, nowSec) : 0,
1294
+ total,
1295
+ upload: 0
1296
+ };
1297
+ }
1298
+ function parseSubscriptionUserinfo(header) {
1299
+ if (!header || header.trim() === "") {
1300
+ return void 0;
1301
+ }
1302
+ const data = { download: 0, expire: 0, total: 0, upload: 0 };
1303
+ let found = false;
1304
+ for (const part of header.split(";")) {
1305
+ const eq = part.indexOf("=");
1306
+ if (eq === -1) {
1307
+ continue;
1308
+ }
1309
+ const key = part.slice(0, eq).trim().toLowerCase();
1310
+ const value = part.slice(eq + 1).trim();
1311
+ if (value === "") {
1312
+ continue;
1313
+ }
1314
+ switch (key) {
1315
+ case "download":
1316
+ case "down":
1317
+ data.download = toNonNegativeNumber(toInt(value));
1318
+ found = true;
1319
+ break;
1320
+ case "expire":
1321
+ case "expires":
1322
+ data.expire = toNonNegativeNumber(toInt(value));
1323
+ found = true;
1324
+ break;
1325
+ case "total":
1326
+ case "limit":
1327
+ data.total = toNonNegativeNumber(toInt(value));
1328
+ found = true;
1329
+ break;
1330
+ case "upload":
1331
+ case "up":
1332
+ data.upload = toNonNegativeNumber(toInt(value));
1333
+ found = true;
1334
+ break;
1335
+ default:
1336
+ break;
1337
+ }
1338
+ }
1339
+ return found ? data : void 0;
1340
+ }
1341
+ function formatSubscriptionUserinfo(data) {
1342
+ const upload = toNonNegativeNumber(data.upload);
1343
+ const download = toNonNegativeNumber(data.download);
1344
+ const total = toNonNegativeNumber(data.total);
1345
+ const expire = toNonNegativeNumber(data.expire);
1346
+ return `upload=${upload}; download=${download}; total=${total}; expire=${expire}`;
1347
+ }
1348
+ function parseGenericJsonUserinfo(raw) {
1349
+ let parsed;
1350
+ try {
1351
+ parsed = JSON.parse(raw);
1352
+ } catch {
1353
+ return void 0;
1354
+ }
1355
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
1356
+ return void 0;
1357
+ }
1358
+ const record = parsed;
1359
+ const pick = (...keys) => {
1360
+ for (const key of keys) {
1361
+ if (record[key] !== void 0) {
1362
+ return record[key];
1363
+ }
1364
+ }
1365
+ return void 0;
1366
+ };
1367
+ const uploadRaw = pick("upload", "upload_b", "up", "u");
1368
+ const downloadRaw = pick(
1369
+ "download",
1370
+ "download_b",
1371
+ "down",
1372
+ "d",
1373
+ "used",
1374
+ "bw_counter_b",
1375
+ "bwCounterB"
1376
+ );
1377
+ const totalRaw = pick(
1378
+ "total",
1379
+ "total_b",
1380
+ "limit",
1381
+ "bandwidth",
1382
+ "monthly_bw_limit_b",
1383
+ "monthlyBwLimitB"
1384
+ );
1385
+ const expireRaw = pick("expire", "expires", "expire_at", "expireAt", "reset");
1386
+ if (uploadRaw === void 0 && downloadRaw === void 0 && totalRaw === void 0) {
1387
+ return void 0;
1388
+ }
1389
+ return {
1390
+ download: toNonNegativeNumber(downloadRaw),
1391
+ expire: toNonNegativeNumber(expireRaw),
1392
+ total: toNonNegativeNumber(totalRaw),
1393
+ upload: toNonNegativeNumber(uploadRaw)
1394
+ };
1395
+ }
1396
+ function convertUserinfo(raw, provider, nowSec) {
1397
+ if (!raw || raw.trim() === "") {
1398
+ return void 0;
1399
+ }
1400
+ const normalized = normalizeUserinfoProvider(provider);
1401
+ if (normalized === "jms") {
1402
+ const jms2 = parseJmsUserinfo(raw, nowSec);
1403
+ if (jms2) {
1404
+ return formatSubscriptionUserinfo(jms2);
1405
+ }
1406
+ const generic2 = parseGenericJsonUserinfo(raw);
1407
+ return generic2 ? formatSubscriptionUserinfo(scaleUserinfoDecimal(generic2)) : void 0;
1408
+ }
1409
+ if (normalized === "subscription") {
1410
+ const parsed = parseSubscriptionUserinfo(raw) ?? parseGenericJsonUserinfo(raw);
1411
+ return parsed ? formatSubscriptionUserinfo(parsed) : void 0;
1412
+ }
1413
+ const jms = parseJmsUserinfo(raw, nowSec);
1414
+ if (jms) {
1415
+ return formatSubscriptionUserinfo(jms);
1416
+ }
1417
+ const generic = parseGenericJsonUserinfo(raw);
1418
+ if (generic) {
1419
+ return formatSubscriptionUserinfo(generic);
1420
+ }
1421
+ const header = parseSubscriptionUserinfo(raw);
1422
+ return header ? formatSubscriptionUserinfo(header) : void 0;
1423
+ }
1424
+
1064
1425
  // src/parser/ciphers.ts
1065
1426
  var SS_CIPHERS = [
1066
1427
  "2022-blake3-aes-128-gcm",
@@ -1120,9 +1481,9 @@ function isFlattenableSsr(method, obfs, protocol) {
1120
1481
  }
1121
1482
 
1122
1483
  // src/parser/construct.ts
1123
- function commonConstruct(node, type, group, remark, server, port, udp, tfo, scv, tls13, underlyingProxy) {
1484
+ function commonConstruct(node, type, group2, remark, server, port, udp, tfo, scv, tls13, underlyingProxy) {
1124
1485
  node.type = type;
1125
- node.group = group;
1486
+ node.group = group2;
1126
1487
  node.remark = remark;
1127
1488
  node.hostname = server;
1128
1489
  node.underlyingProxy = underlyingProxy;
@@ -1138,7 +1499,7 @@ function vmessConstruct(node, options) {
1138
1499
  aid = 0,
1139
1500
  cipher = "",
1140
1501
  edge = "",
1141
- group,
1502
+ group: group2,
1142
1503
  host = "",
1143
1504
  id = "",
1144
1505
  net = "",
@@ -1157,7 +1518,7 @@ function vmessConstruct(node, options) {
1157
1518
  commonConstruct(
1158
1519
  node,
1159
1520
  "vmess" /* VMess */,
1160
- group,
1521
+ group2,
1161
1522
  remark,
1162
1523
  add,
1163
1524
  port,
@@ -1454,6 +1815,8 @@ function vlessConstruct(node, options) {
1454
1815
  node.host = options.host ?? "";
1455
1816
  node.path = options.path ?? "";
1456
1817
  node.fingerprint = options.fingerprint ?? "";
1818
+ node.publicKey = options.publicKey ?? "";
1819
+ node.shortId = options.shortId ?? "";
1457
1820
  if (options.alpn) {
1458
1821
  node.alpn = options.alpn;
1459
1822
  }
@@ -1503,7 +1866,7 @@ function explodeNetch(netch, node) {
1503
1866
  return;
1504
1867
  }
1505
1868
  const type = str(data.Type);
1506
- const group = str(data.Group);
1869
+ const group2 = str(data.Group);
1507
1870
  let remark = str(data.Remark);
1508
1871
  const address = str(data.Hostname);
1509
1872
  const udp = tri(data.EnableUDP);
@@ -1521,7 +1884,7 @@ function explodeNetch(netch, node) {
1521
1884
  switch (type) {
1522
1885
  case "SS":
1523
1886
  ssConstruct(node, {
1524
- group: group === "" ? DEFAULT_GROUPS.ss : group,
1887
+ group: group2 === "" ? DEFAULT_GROUPS.ss : group2,
1525
1888
  method,
1526
1889
  password,
1527
1890
  plugin: str(data.Plugin),
@@ -1539,7 +1902,7 @@ function explodeNetch(netch, node) {
1539
1902
  const obfs = str(data.OBFS);
1540
1903
  if (isFlattenableSsr(method, obfs, protocol)) {
1541
1904
  ssConstruct(node, {
1542
- group: group === "" ? DEFAULT_GROUPS.ss : group,
1905
+ group: group2 === "" ? DEFAULT_GROUPS.ss : group2,
1543
1906
  method,
1544
1907
  password,
1545
1908
  plugin: str(data.Plugin),
@@ -1553,7 +1916,7 @@ function explodeNetch(netch, node) {
1553
1916
  });
1554
1917
  } else {
1555
1918
  ssrConstruct(node, {
1556
- group: group === "" ? DEFAULT_GROUPS.ssr : group,
1919
+ group: group2 === "" ? DEFAULT_GROUPS.ssr : group2,
1557
1920
  method,
1558
1921
  obfs,
1559
1922
  obfsParam: str(data.OBFSParam),
@@ -1576,7 +1939,7 @@ function explodeNetch(netch, node) {
1576
1939
  aid: str(data.AlterID),
1577
1940
  cipher: method,
1578
1941
  edge: str(data.Edge),
1579
- group: group === "" ? DEFAULT_GROUPS.v2ray : group,
1942
+ group: group2 === "" ? DEFAULT_GROUPS.v2ray : group2,
1580
1943
  host: str(data.Host),
1581
1944
  id: str(data.UserID),
1582
1945
  net: str(data.TransferProtocol),
@@ -1593,7 +1956,7 @@ function explodeNetch(netch, node) {
1593
1956
  break;
1594
1957
  case "Socks5":
1595
1958
  socksConstruct(node, {
1596
- group: group === "" ? DEFAULT_GROUPS.socks : group,
1959
+ group: group2 === "" ? DEFAULT_GROUPS.socks : group2,
1597
1960
  password,
1598
1961
  port,
1599
1962
  remark,
@@ -1607,7 +1970,7 @@ function explodeNetch(netch, node) {
1607
1970
  case "HTTP":
1608
1971
  case "HTTPS":
1609
1972
  httpConstruct(node, {
1610
- group: group === "" ? DEFAULT_GROUPS.http : group,
1973
+ group: group2 === "" ? DEFAULT_GROUPS.http : group2,
1611
1974
  password,
1612
1975
  port,
1613
1976
  remark,
@@ -1619,7 +1982,7 @@ function explodeNetch(netch, node) {
1619
1982
  break;
1620
1983
  case "Trojan":
1621
1984
  trojanConstruct(node, {
1622
- group: group === "" ? DEFAULT_GROUPS.trojan : group,
1985
+ group: group2 === "" ? DEFAULT_GROUPS.trojan : group2,
1623
1986
  host: str(data.Host),
1624
1987
  network: str(data.TransferProtocol),
1625
1988
  password,
@@ -1635,7 +1998,7 @@ function explodeNetch(netch, node) {
1635
1998
  break;
1636
1999
  case "Snell":
1637
2000
  snellConstruct(node, {
1638
- group: group === "" ? DEFAULT_GROUPS.snell : group,
2001
+ group: group2 === "" ? DEFAULT_GROUPS.snell : group2,
1639
2002
  host: str(data.Host),
1640
2003
  obfs: str(data.OBFS),
1641
2004
  password,
@@ -1699,10 +2062,10 @@ function explodeClash(root, nodes) {
1699
2062
  const tfo = asTri(get(single, "fast-open"));
1700
2063
  const scv = asTri(get(single, "skip-cert-verify"));
1701
2064
  const node = createProxy();
1702
- let group = "";
2065
+ let group2 = "";
1703
2066
  switch (proxyType) {
1704
2067
  case "vmess": {
1705
- group = DEFAULT_GROUPS.v2ray;
2068
+ group2 = DEFAULT_GROUPS.v2ray;
1706
2069
  const id = asString(get(single, "uuid"));
1707
2070
  const aid = asString(get(single, "alterId"));
1708
2071
  const cipher = asString(get(single, "cipher"));
@@ -1748,7 +2111,7 @@ function explodeClash(root, nodes) {
1748
2111
  aid,
1749
2112
  cipher,
1750
2113
  edge,
1751
- group,
2114
+ group: group2,
1752
2115
  host,
1753
2116
  id,
1754
2117
  net,
@@ -1766,7 +2129,7 @@ function explodeClash(root, nodes) {
1766
2129
  break;
1767
2130
  }
1768
2131
  case "ss": {
1769
- group = DEFAULT_GROUPS.ss;
2132
+ group2 = DEFAULT_GROUPS.ss;
1770
2133
  let cipher = asString(get(single, "cipher"));
1771
2134
  const password = asString(get(single, "password"));
1772
2135
  let plugin = "";
@@ -1817,7 +2180,7 @@ function explodeClash(root, nodes) {
1817
2180
  cipher = cipher.replace(/AEAD_/g, "").replace(/_/g, "-").toLowerCase();
1818
2181
  }
1819
2182
  ssConstruct(node, {
1820
- group,
2183
+ group: group2,
1821
2184
  method: cipher,
1822
2185
  password,
1823
2186
  plugin,
@@ -1833,11 +2196,11 @@ function explodeClash(root, nodes) {
1833
2196
  break;
1834
2197
  }
1835
2198
  case "socks5": {
1836
- group = DEFAULT_GROUPS.socks;
2199
+ group2 = DEFAULT_GROUPS.socks;
1837
2200
  const user = asString(get(single, "username"));
1838
2201
  const password = asString(get(single, "password"));
1839
2202
  socksConstruct(node, {
1840
- group,
2203
+ group: group2,
1841
2204
  password,
1842
2205
  port,
1843
2206
  remark: ps,
@@ -1848,7 +2211,7 @@ function explodeClash(root, nodes) {
1848
2211
  break;
1849
2212
  }
1850
2213
  case "ssr": {
1851
- group = DEFAULT_GROUPS.ssr;
2214
+ group2 = DEFAULT_GROUPS.ssr;
1852
2215
  let cipher = asString(get(single, "cipher"));
1853
2216
  if (cipher === "dummy") {
1854
2217
  cipher = "none";
@@ -1859,7 +2222,7 @@ function explodeClash(root, nodes) {
1859
2222
  const protocolParam = get(single, "protocol-param") !== void 0 ? asString(get(single, "protocol-param")) : asString(get(single, "protocolparam"));
1860
2223
  const obfsParam = get(single, "obfs-param") !== void 0 ? asString(get(single, "obfs-param")) : asString(get(single, "obfsparam"));
1861
2224
  ssrConstruct(node, {
1862
- group,
2225
+ group: group2,
1863
2226
  method: cipher,
1864
2227
  obfs,
1865
2228
  obfsParam,
@@ -1877,12 +2240,12 @@ function explodeClash(root, nodes) {
1877
2240
  break;
1878
2241
  }
1879
2242
  case "http": {
1880
- group = DEFAULT_GROUPS.http;
2243
+ group2 = DEFAULT_GROUPS.http;
1881
2244
  const user = asString(get(single, "username"));
1882
2245
  const password = asString(get(single, "password"));
1883
2246
  const tls = asString(get(single, "tls"));
1884
2247
  httpConstruct(node, {
1885
- group,
2248
+ group: group2,
1886
2249
  password,
1887
2250
  port,
1888
2251
  remark: ps,
@@ -1896,7 +2259,7 @@ function explodeClash(root, nodes) {
1896
2259
  break;
1897
2260
  }
1898
2261
  case "trojan": {
1899
- group = DEFAULT_GROUPS.trojan;
2262
+ group2 = DEFAULT_GROUPS.trojan;
1900
2263
  const password = asString(get(single, "password"));
1901
2264
  const host = asString(get(single, "sni"));
1902
2265
  let net = asString(get(single, "network"));
@@ -1914,7 +2277,7 @@ function explodeClash(root, nodes) {
1914
2277
  break;
1915
2278
  }
1916
2279
  trojanConstruct(node, {
1917
- group,
2280
+ group: group2,
1918
2281
  host,
1919
2282
  network: net,
1920
2283
  password,
@@ -1931,13 +2294,13 @@ function explodeClash(root, nodes) {
1931
2294
  break;
1932
2295
  }
1933
2296
  case "snell": {
1934
- group = DEFAULT_GROUPS.snell;
2297
+ group2 = DEFAULT_GROUPS.snell;
1935
2298
  const password = asString(get(single, "psk"));
1936
2299
  const obfs = asString(get(get(single, "obfs-opts"), "mode"));
1937
2300
  const host = asString(get(get(single, "obfs-opts"), "host"));
1938
2301
  const version = asString(get(single, "version"));
1939
2302
  snellConstruct(node, {
1940
- group,
2303
+ group: group2,
1941
2304
  host,
1942
2305
  obfs,
1943
2306
  password,
@@ -1953,7 +2316,7 @@ function explodeClash(root, nodes) {
1953
2316
  break;
1954
2317
  }
1955
2318
  case "wireguard": {
1956
- group = DEFAULT_GROUPS.wireguard;
2319
+ group2 = DEFAULT_GROUPS.wireguard;
1957
2320
  const publicKey = asString(get(single, "public-key"));
1958
2321
  const privateKey = asString(get(single, "private-key"));
1959
2322
  const dns = get(single, "dns");
@@ -1964,7 +2327,7 @@ function explodeClash(root, nodes) {
1964
2327
  const ipv6 = asString(get(single, "ipv6"));
1965
2328
  wireGuardConstruct(node, {
1966
2329
  dnsServers,
1967
- group,
2330
+ group: group2,
1968
2331
  keepAlive: "0",
1969
2332
  mtu,
1970
2333
  port,
@@ -1982,7 +2345,7 @@ function explodeClash(root, nodes) {
1982
2345
  break;
1983
2346
  }
1984
2347
  case "hysteria": {
1985
- group = DEFAULT_GROUPS.hysteria;
2348
+ group2 = DEFAULT_GROUPS.hysteria;
1986
2349
  const ports = asString(get(single, "ports"));
1987
2350
  const protocol = asString(get(single, "protocol"));
1988
2351
  const obfsProtocol = asString(get(single, "obfs-protocol"));
@@ -2019,7 +2382,7 @@ function explodeClash(root, nodes) {
2019
2382
  down,
2020
2383
  downSpeed,
2021
2384
  fingerprint,
2022
- group,
2385
+ group: group2,
2023
2386
  hopInterval,
2024
2387
  obfs,
2025
2388
  obfsProtocol,
@@ -2040,7 +2403,7 @@ function explodeClash(root, nodes) {
2040
2403
  break;
2041
2404
  }
2042
2405
  case "hysteria2": {
2043
- group = DEFAULT_GROUPS.hysteria2;
2406
+ group2 = DEFAULT_GROUPS.hysteria2;
2044
2407
  const ports = asString(get(single, "ports"));
2045
2408
  const up = asString(get(single, "up"));
2046
2409
  const down = asString(get(single, "down"));
@@ -2065,7 +2428,7 @@ function explodeClash(root, nodes) {
2065
2428
  cwnd,
2066
2429
  down,
2067
2430
  fingerprint,
2068
- group,
2431
+ group: group2,
2069
2432
  hopInterval,
2070
2433
  obfs,
2071
2434
  obfsPassword,
@@ -2083,11 +2446,11 @@ function explodeClash(root, nodes) {
2083
2446
  break;
2084
2447
  }
2085
2448
  case "anytls": {
2086
- group = DEFAULT_GROUPS.anytls;
2449
+ group2 = DEFAULT_GROUPS.anytls;
2087
2450
  const password = asString(get(single, "password"));
2088
2451
  const sni = asString(get(single, "sni"));
2089
2452
  anyTlsConstruct(node, {
2090
- group,
2453
+ group: group2,
2091
2454
  password,
2092
2455
  port,
2093
2456
  remark: ps,
@@ -2101,7 +2464,7 @@ function explodeClash(root, nodes) {
2101
2464
  break;
2102
2465
  }
2103
2466
  case "vless": {
2104
- group = DEFAULT_GROUPS.v2ray;
2467
+ group2 = DEFAULT_GROUPS.v2ray;
2105
2468
  const userId = asString(get(single, "uuid"));
2106
2469
  const net = asString(get(single, "network"));
2107
2470
  const tls = asString(get(single, "tls"));
@@ -2119,7 +2482,7 @@ function explodeClash(root, nodes) {
2119
2482
  encryption: asString(get(single, "encryption")),
2120
2483
  fingerprint: asString(get(single, "client-fingerprint")),
2121
2484
  flow: asString(get(single, "flow")),
2122
- group,
2485
+ group: group2,
2123
2486
  host,
2124
2487
  network: net,
2125
2488
  path,
@@ -2137,12 +2500,12 @@ function explodeClash(root, nodes) {
2137
2500
  break;
2138
2501
  }
2139
2502
  case "tuic": {
2140
- group = DEFAULT_GROUPS.v2ray;
2503
+ group2 = DEFAULT_GROUPS.v2ray;
2141
2504
  const alpnValue = get(single, "alpn");
2142
2505
  tuicConstruct(node, {
2143
2506
  alpn: Array.isArray(alpnValue) ? alpnValue.map((a) => String(a)) : void 0,
2144
2507
  congestionControl: asString(get(single, "congestion-controller")),
2145
- group,
2508
+ group: group2,
2146
2509
  password: asString(get(single, "password")),
2147
2510
  port,
2148
2511
  remark: ps,
@@ -2209,8 +2572,8 @@ function collectKeyValues(lines, startIndex, sink) {
2209
2572
  sink[trim(line.slice(0, eq))] = trim(line.slice(eq + 1));
2210
2573
  }
2211
2574
  }
2212
- function explodeSurge(surge, nodes) {
2213
- let text = surge;
2575
+ function explodeSurge(surge2, nodes) {
2576
+ let text = surge2;
2214
2577
  if (text.includes("[Proxy]")) {
2215
2578
  text = text.replace(/^[\s\S]*?\[/, "[");
2216
2579
  }
@@ -3035,7 +3398,7 @@ function explodeSsConf(content, nodes) {
3035
3398
  if (!(section in json)) {
3036
3399
  return;
3037
3400
  }
3038
- const group = asString(json.remarks);
3401
+ const group2 = asString(json.remarks);
3039
3402
  const list = json[section];
3040
3403
  if (!Array.isArray(list)) {
3041
3404
  return;
@@ -3050,7 +3413,7 @@ function explodeSsConf(content, nodes) {
3050
3413
  const server = asString(entry.server);
3051
3414
  const node = createProxy();
3052
3415
  ssConstruct(node, {
3053
- group,
3416
+ group: group2,
3054
3417
  method: asString(entry.method),
3055
3418
  password: asString(entry.password),
3056
3419
  plugin: asString(entry.plugin),
@@ -3155,7 +3518,7 @@ function explodeSsrConf(content, nodes) {
3155
3518
  return;
3156
3519
  }
3157
3520
  for (const entry of list) {
3158
- const group = asString(entry.group) === "" ? DEFAULT_GROUPS.ssr : asString(entry.group);
3521
+ const group2 = asString(entry.group) === "" ? DEFAULT_GROUPS.ssr : asString(entry.group);
3159
3522
  const server = asString(entry.server);
3160
3523
  const port = asString(entry.server_port);
3161
3524
  if (port === "0" || port === "") {
@@ -3164,7 +3527,7 @@ function explodeSsrConf(content, nodes) {
3164
3527
  const remark = asString(entry.remarks) === "" ? `${server}:${port}` : asString(entry.remarks);
3165
3528
  const node = createProxy();
3166
3529
  ssrConstruct(node, {
3167
- group,
3530
+ group: group2,
3168
3531
  method: asString(entry.method),
3169
3532
  obfs: asString(entry.obfs),
3170
3533
  obfsParam: asString(entry.obfsparam),
@@ -3269,9 +3632,9 @@ function explodeVmessConf(content, nodes) {
3269
3632
  continue;
3270
3633
  }
3271
3634
  const subid = asString(entry.subid);
3272
- let group = "";
3635
+ let group2 = "";
3273
3636
  if (subid !== "") {
3274
- group = subdata.get(subid) ?? "";
3637
+ group2 = subdata.get(subid) ?? "";
3275
3638
  }
3276
3639
  if (ps === "") {
3277
3640
  ps = `${add}:${port}`;
@@ -3284,7 +3647,7 @@ function explodeVmessConf(content, nodes) {
3284
3647
  add,
3285
3648
  aid: asString(entry.alterId),
3286
3649
  cipher: asString(entry.security),
3287
- group: group || DEFAULT_GROUPS.v2ray,
3650
+ group: group2 || DEFAULT_GROUPS.v2ray,
3288
3651
  host: asString(entry.requestHost),
3289
3652
  id: asString(entry.id),
3290
3653
  net: asString(entry.network),
@@ -3298,7 +3661,7 @@ function explodeVmessConf(content, nodes) {
3298
3661
  });
3299
3662
  } else if (configType === 3) {
3300
3663
  ssConstruct(node, {
3301
- group: group || DEFAULT_GROUPS.ss,
3664
+ group: group2 || DEFAULT_GROUPS.ss,
3302
3665
  method: asString(entry.security),
3303
3666
  password: asString(entry.id),
3304
3667
  port,
@@ -3308,7 +3671,7 @@ function explodeVmessConf(content, nodes) {
3308
3671
  });
3309
3672
  } else if (configType === 4) {
3310
3673
  socksConstruct(node, {
3311
- group: group || DEFAULT_GROUPS.socks,
3674
+ group: group2 || DEFAULT_GROUPS.socks,
3312
3675
  port,
3313
3676
  remark: ps,
3314
3677
  scv,
@@ -3334,7 +3697,7 @@ function explodeSstap(content, nodes) {
3334
3697
  }
3335
3698
  let index = nodes.length;
3336
3699
  for (const entry of json.configs) {
3337
- const group = asString(entry.group);
3700
+ const group2 = asString(entry.group);
3338
3701
  const server = asString(entry.server);
3339
3702
  const port = asString(entry.server_port);
3340
3703
  if (port === "0" || port === "") {
@@ -3346,7 +3709,7 @@ function explodeSstap(content, nodes) {
3346
3709
  const configType = toInt(entry.type);
3347
3710
  if (configType === 5) {
3348
3711
  socksConstruct(node, {
3349
- group,
3712
+ group: group2,
3350
3713
  password,
3351
3714
  port,
3352
3715
  remark,
@@ -3358,10 +3721,10 @@ function explodeSstap(content, nodes) {
3358
3721
  const obfs = asString(entry.obfs);
3359
3722
  const method = asString(entry.method);
3360
3723
  if (isFlattenableSsr(method, obfs, protocol)) {
3361
- ssConstruct(node, { group, method, password, port, remark, server });
3724
+ ssConstruct(node, { group: group2, method, password, port, remark, server });
3362
3725
  } else {
3363
3726
  ssrConstruct(node, {
3364
- group,
3727
+ group: group2,
3365
3728
  method,
3366
3729
  obfs,
3367
3730
  obfsParam: asString(entry.obfsparam),
@@ -3667,7 +4030,7 @@ function explodeQuan(quan, node) {
3667
4030
  }
3668
4031
  const cipher = trim(configs[4]);
3669
4032
  const id = trim(configs[5].replace(/"/g, ""));
3670
- let group = DEFAULT_GROUPS.v2ray;
4033
+ let group2 = DEFAULT_GROUPS.v2ray;
3671
4034
  let tls = "";
3672
4035
  let host = "";
3673
4036
  let path = "";
@@ -3682,7 +4045,7 @@ function explodeQuan(quan, node) {
3682
4045
  const itemVal = trim(vArray.slice(1).join("="));
3683
4046
  switch (itemName) {
3684
4047
  case "group":
3685
- group = itemVal;
4048
+ group2 = itemVal;
3686
4049
  break;
3687
4050
  case "over-tls":
3688
4051
  tls = itemVal === "true" ? "tls" : "";
@@ -3721,7 +4084,7 @@ function explodeQuan(quan, node) {
3721
4084
  aid: "0",
3722
4085
  cipher,
3723
4086
  edge,
3724
- group,
4087
+ group: group2,
3725
4088
  host,
3726
4089
  id,
3727
4090
  net,
@@ -3734,12 +4097,12 @@ function explodeQuan(quan, node) {
3734
4097
  }
3735
4098
  function explodeSs(ss, node) {
3736
4099
  let body = ss.slice(5).replace(/\/\?/g, "?");
3737
- const group = DEFAULT_GROUPS.ss;
4100
+ const group2 = DEFAULT_GROUPS.ss;
3738
4101
  let remark = "";
3739
4102
  let addition = "";
3740
4103
  let plugin = "";
3741
4104
  let pluginOption = "";
3742
- let resolvedGroup = group;
4105
+ let resolvedGroup = group2;
3743
4106
  const hashPos = body.indexOf("#");
3744
4107
  if (hashPos !== -1) {
3745
4108
  remark = urlDecode(body.slice(hashPos + 1));
@@ -3806,7 +4169,7 @@ function explodeSs(ss, node) {
3806
4169
  function explodeSsr(ssr, node) {
3807
4170
  let body = ssr.slice(6).replace(/\r/g, "");
3808
4171
  body = urlSafeBase64Decode(body);
3809
- let group = "";
4172
+ let group2 = "";
3810
4173
  let remark = "";
3811
4174
  let obfsParam = "";
3812
4175
  let protocolParam = "";
@@ -3814,7 +4177,7 @@ function explodeSsr(ssr, node) {
3814
4177
  if (obfsPos !== -1) {
3815
4178
  const strobfs = body.slice(obfsPos + 2);
3816
4179
  body = body.slice(0, obfsPos);
3817
- group = urlSafeBase64Decode(getUrlArg(strobfs, "group"));
4180
+ group2 = urlSafeBase64Decode(getUrlArg(strobfs, "group"));
3818
4181
  remark = urlSafeBase64Decode(getUrlArg(strobfs, "remarks"));
3819
4182
  obfsParam = urlSafeBase64Decode(getUrlArg(strobfs, "obfsparam")).replace(/\s/g, "");
3820
4183
  protocolParam = urlSafeBase64Decode(getUrlArg(strobfs, "protoparam")).replace(/\s/g, "");
@@ -3832,7 +4195,7 @@ function explodeSsr(ssr, node) {
3832
4195
  if (port === "0") {
3833
4196
  return;
3834
4197
  }
3835
- const resolvedGroup = group === "" ? DEFAULT_GROUPS.ssr : group;
4198
+ const resolvedGroup = group2 === "" ? DEFAULT_GROUPS.ssr : group2;
3836
4199
  const resolvedRemark = remark === "" ? `${server}:${port}` : remark;
3837
4200
  if (isFlattenableSsr(method, obfs, protocol)) {
3838
4201
  ssConstruct(node, {
@@ -3870,7 +4233,7 @@ function explodeSsd(link, nodes) {
3870
4233
  if (data === null || typeof data !== "object" || !("servers" in data)) {
3871
4234
  return;
3872
4235
  }
3873
- const group = String(data.airport ?? "");
4236
+ const group2 = String(data.airport ?? "");
3874
4237
  const servers = data.servers;
3875
4238
  let entries;
3876
4239
  if (Array.isArray(servers)) {
@@ -3896,7 +4259,7 @@ function explodeSsd(link, nodes) {
3896
4259
  const pluginOption = String(entry.plugin_options ?? data.plugin_options ?? "");
3897
4260
  const node = createProxy();
3898
4261
  ssConstruct(node, {
3899
- group,
4262
+ group: group2,
3900
4263
  method,
3901
4264
  password,
3902
4265
  plugin,
@@ -3911,7 +4274,7 @@ function explodeSsd(link, nodes) {
3911
4274
  }
3912
4275
  }
3913
4276
  function explodeSocks(link, node) {
3914
- let group = "";
4277
+ let group2 = "";
3915
4278
  let remark = "";
3916
4279
  let server = "";
3917
4280
  let port = "";
@@ -3950,10 +4313,10 @@ function explodeSocks(link, node) {
3950
4313
  username = urlDecode(getUrlArg(link, "user"));
3951
4314
  password = urlDecode(getUrlArg(link, "pass"));
3952
4315
  remark = urlDecode(getUrlArg(link, "remarks"));
3953
- group = urlDecode(getUrlArg(link, "group"));
4316
+ group2 = urlDecode(getUrlArg(link, "group"));
3954
4317
  }
3955
- if (group === "") {
3956
- group = DEFAULT_GROUPS.socks;
4318
+ if (group2 === "") {
4319
+ group2 = DEFAULT_GROUPS.socks;
3957
4320
  }
3958
4321
  if (remark === "") {
3959
4322
  remark = `${server}:${port}`;
@@ -3961,7 +4324,7 @@ function explodeSocks(link, node) {
3961
4324
  if (port === "0" || port === "") {
3962
4325
  return;
3963
4326
  }
3964
- socksConstruct(node, { group, password, port, remark, server, username });
4327
+ socksConstruct(node, { group: group2, password, port, remark, server, username });
3965
4328
  }
3966
4329
  function explodeHttp(link, node) {
3967
4330
  const server = getUrlArg(link, "server");
@@ -3969,9 +4332,9 @@ function explodeHttp(link, node) {
3969
4332
  const username = urlDecode(getUrlArg(link, "user"));
3970
4333
  const password = urlDecode(getUrlArg(link, "pass"));
3971
4334
  let remark = urlDecode(getUrlArg(link, "remarks"));
3972
- let group = urlDecode(getUrlArg(link, "group"));
3973
- if (group === "") {
3974
- group = DEFAULT_GROUPS.http;
4335
+ let group2 = urlDecode(getUrlArg(link, "group"));
4336
+ if (group2 === "") {
4337
+ group2 = DEFAULT_GROUPS.http;
3975
4338
  }
3976
4339
  if (remark === "") {
3977
4340
  remark = `${server}:${port}`;
@@ -3980,7 +4343,7 @@ function explodeHttp(link, node) {
3980
4343
  return;
3981
4344
  }
3982
4345
  httpConstruct(node, {
3983
- group,
4346
+ group: group2,
3984
4347
  password,
3985
4348
  port,
3986
4349
  remark,
@@ -3992,14 +4355,14 @@ function explodeHttp(link, node) {
3992
4355
  function explodeHttpSub(link, node) {
3993
4356
  let body = link;
3994
4357
  let remarks = "";
3995
- let group = "";
4358
+ let group2 = "";
3996
4359
  const tls = body.includes("https://");
3997
4360
  const qpos = body.indexOf("?");
3998
4361
  if (qpos !== -1) {
3999
4362
  const addition = body.slice(qpos + 1);
4000
4363
  body = body.slice(0, qpos);
4001
4364
  remarks = urlDecode(getUrlArg(addition, "remarks"));
4002
- group = urlDecode(getUrlArg(addition, "group"));
4365
+ group2 = urlDecode(getUrlArg(addition, "group"));
4003
4366
  }
4004
4367
  body = body.slice(body.indexOf("://") + 3);
4005
4368
  body = urlSafeBase64Decode(body);
@@ -4024,8 +4387,8 @@ function explodeHttpSub(link, node) {
4024
4387
  server = match[1];
4025
4388
  port = match[2];
4026
4389
  }
4027
- if (group === "") {
4028
- group = DEFAULT_GROUPS.http;
4390
+ if (group2 === "") {
4391
+ group2 = DEFAULT_GROUPS.http;
4029
4392
  }
4030
4393
  if (remarks === "") {
4031
4394
  remarks = `${server}:${port}`;
@@ -4034,7 +4397,7 @@ function explodeHttpSub(link, node) {
4034
4397
  return;
4035
4398
  }
4036
4399
  httpConstruct(node, {
4037
- group,
4400
+ group: group2,
4038
4401
  password,
4039
4402
  port,
4040
4403
  remark: remarks,
@@ -4075,7 +4438,7 @@ function explodeTrojan(trojan, node) {
4075
4438
  host = getUrlArg(addition, "peer");
4076
4439
  }
4077
4440
  const tfo = getUrlArg(addition, "tfo");
4078
- const group = urlDecode(getUrlArg(addition, "group"));
4441
+ const group2 = urlDecode(getUrlArg(addition, "group"));
4079
4442
  if (getUrlArg(addition, "ws") === "1") {
4080
4443
  path = getUrlArg(addition, "wspath");
4081
4444
  network = "ws";
@@ -4093,7 +4456,7 @@ function explodeTrojan(trojan, node) {
4093
4456
  remark = `${server}:${port}`;
4094
4457
  }
4095
4458
  trojanConstruct(node, {
4096
- group: group === "" ? DEFAULT_GROUPS.trojan : group,
4459
+ group: group2 === "" ? DEFAULT_GROUPS.trojan : group2,
4097
4460
  host,
4098
4461
  network,
4099
4462
  password,
@@ -4262,10 +4625,14 @@ function explodeVless(link, node) {
4262
4625
  const network = getUrlArg(addition, "type");
4263
4626
  const security = getUrlArg(addition, "security");
4264
4627
  const host = getUrlArg(addition, "host");
4265
- const path = urlDecode(getUrlArg(addition, "path"));
4628
+ const rawPath = urlDecode(getUrlArg(addition, "path"));
4629
+ const serviceName = urlDecode(getUrlArg(addition, "serviceName"));
4630
+ const path = rawPath !== "" ? rawPath : serviceName;
4266
4631
  const sni = getUrlArg(addition, "sni");
4267
4632
  const flow = getUrlArg(addition, "flow");
4268
4633
  const fingerprint = getUrlArg(addition, "fp");
4634
+ const publicKey = getUrlArg(addition, "pbk");
4635
+ const shortId = getUrlArg(addition, "sid");
4269
4636
  const alpn = getUrlArg(addition, "alpn");
4270
4637
  if (remark === "") {
4271
4638
  remark = `${server}:${port}`;
@@ -4279,9 +4646,11 @@ function explodeVless(link, node) {
4279
4646
  network,
4280
4647
  path,
4281
4648
  port,
4649
+ publicKey,
4282
4650
  remark,
4283
4651
  scv: getUrlArg(addition, "allowInsecure") === "" ? void 0 : parseTri(getUrlArg(addition, "allowInsecure")),
4284
4652
  server,
4653
+ shortId,
4285
4654
  sni,
4286
4655
  tls: security,
4287
4656
  userId
@@ -4422,6 +4791,425 @@ function parseSubscription(content) {
4422
4791
  // src/generator/clash.ts
4423
4792
  var import_yaml2 = require("yaml");
4424
4793
 
4794
+ // src/utils/country.ts
4795
+ var REGION_CODES = [
4796
+ "AD",
4797
+ "AE",
4798
+ "AF",
4799
+ "AG",
4800
+ "AI",
4801
+ "AL",
4802
+ "AM",
4803
+ "AO",
4804
+ "AQ",
4805
+ "AR",
4806
+ "AS",
4807
+ "AT",
4808
+ "AU",
4809
+ "AW",
4810
+ "AX",
4811
+ "AZ",
4812
+ "BA",
4813
+ "BB",
4814
+ "BD",
4815
+ "BE",
4816
+ "BF",
4817
+ "BG",
4818
+ "BH",
4819
+ "BI",
4820
+ "BJ",
4821
+ "BL",
4822
+ "BM",
4823
+ "BN",
4824
+ "BO",
4825
+ "BQ",
4826
+ "BR",
4827
+ "BS",
4828
+ "BT",
4829
+ "BV",
4830
+ "BW",
4831
+ "BY",
4832
+ "BZ",
4833
+ "CA",
4834
+ "CC",
4835
+ "CD",
4836
+ "CF",
4837
+ "CG",
4838
+ "CH",
4839
+ "CI",
4840
+ "CK",
4841
+ "CL",
4842
+ "CM",
4843
+ "CN",
4844
+ "CO",
4845
+ "CR",
4846
+ "CU",
4847
+ "CV",
4848
+ "CW",
4849
+ "CX",
4850
+ "CY",
4851
+ "CZ",
4852
+ "DE",
4853
+ "DJ",
4854
+ "DK",
4855
+ "DM",
4856
+ "DO",
4857
+ "DZ",
4858
+ "EC",
4859
+ "EE",
4860
+ "EG",
4861
+ "EH",
4862
+ "ER",
4863
+ "ES",
4864
+ "ET",
4865
+ "FI",
4866
+ "FJ",
4867
+ "FK",
4868
+ "FM",
4869
+ "FO",
4870
+ "FR",
4871
+ "GA",
4872
+ "GB",
4873
+ "GD",
4874
+ "GE",
4875
+ "GF",
4876
+ "GG",
4877
+ "GH",
4878
+ "GI",
4879
+ "GL",
4880
+ "GM",
4881
+ "GN",
4882
+ "GP",
4883
+ "GQ",
4884
+ "GR",
4885
+ "GS",
4886
+ "GT",
4887
+ "GU",
4888
+ "GW",
4889
+ "GY",
4890
+ "HK",
4891
+ "HM",
4892
+ "HN",
4893
+ "HR",
4894
+ "HT",
4895
+ "HU",
4896
+ "ID",
4897
+ "IE",
4898
+ "IL",
4899
+ "IM",
4900
+ "IN",
4901
+ "IO",
4902
+ "IQ",
4903
+ "IR",
4904
+ "IS",
4905
+ "IT",
4906
+ "JE",
4907
+ "JM",
4908
+ "JO",
4909
+ "JP",
4910
+ "KE",
4911
+ "KG",
4912
+ "KH",
4913
+ "KI",
4914
+ "KM",
4915
+ "KN",
4916
+ "KP",
4917
+ "KR",
4918
+ "KW",
4919
+ "KY",
4920
+ "KZ",
4921
+ "LA",
4922
+ "LB",
4923
+ "LC",
4924
+ "LI",
4925
+ "LK",
4926
+ "LR",
4927
+ "LS",
4928
+ "LT",
4929
+ "LU",
4930
+ "LV",
4931
+ "LY",
4932
+ "MA",
4933
+ "MC",
4934
+ "MD",
4935
+ "ME",
4936
+ "MF",
4937
+ "MG",
4938
+ "MH",
4939
+ "MK",
4940
+ "ML",
4941
+ "MM",
4942
+ "MN",
4943
+ "MO",
4944
+ "MP",
4945
+ "MQ",
4946
+ "MR",
4947
+ "MS",
4948
+ "MT",
4949
+ "MU",
4950
+ "MV",
4951
+ "MW",
4952
+ "MX",
4953
+ "MY",
4954
+ "MZ",
4955
+ "NA",
4956
+ "NC",
4957
+ "NE",
4958
+ "NF",
4959
+ "NG",
4960
+ "NI",
4961
+ "NL",
4962
+ "NO",
4963
+ "NP",
4964
+ "NR",
4965
+ "NU",
4966
+ "NZ",
4967
+ "OM",
4968
+ "PA",
4969
+ "PE",
4970
+ "PF",
4971
+ "PG",
4972
+ "PH",
4973
+ "PK",
4974
+ "PL",
4975
+ "PM",
4976
+ "PN",
4977
+ "PR",
4978
+ "PS",
4979
+ "PT",
4980
+ "PW",
4981
+ "PY",
4982
+ "QA",
4983
+ "RE",
4984
+ "RO",
4985
+ "RS",
4986
+ "RU",
4987
+ "RW",
4988
+ "SA",
4989
+ "SB",
4990
+ "SC",
4991
+ "SD",
4992
+ "SE",
4993
+ "SG",
4994
+ "SH",
4995
+ "SI",
4996
+ "SJ",
4997
+ "SK",
4998
+ "SL",
4999
+ "SM",
5000
+ "SN",
5001
+ "SO",
5002
+ "SR",
5003
+ "SS",
5004
+ "ST",
5005
+ "SV",
5006
+ "SX",
5007
+ "SY",
5008
+ "SZ",
5009
+ "TC",
5010
+ "TD",
5011
+ "TF",
5012
+ "TG",
5013
+ "TH",
5014
+ "TJ",
5015
+ "TK",
5016
+ "TL",
5017
+ "TM",
5018
+ "TN",
5019
+ "TO",
5020
+ "TR",
5021
+ "TT",
5022
+ "TV",
5023
+ "TW",
5024
+ "TZ",
5025
+ "UA",
5026
+ "UG",
5027
+ "UM",
5028
+ "US",
5029
+ "UY",
5030
+ "UZ",
5031
+ "VA",
5032
+ "VC",
5033
+ "VE",
5034
+ "VG",
5035
+ "VI",
5036
+ "VN",
5037
+ "VU",
5038
+ "WF",
5039
+ "WS",
5040
+ "YE",
5041
+ "YT",
5042
+ "ZA",
5043
+ "ZM",
5044
+ "ZW"
5045
+ ];
5046
+ var REGION_CODE_SET = new Set(REGION_CODES);
5047
+ var ZH_OVERRIDES = {
5048
+ AE: "\u963F\u8054\u914B",
5049
+ BN: "\u6587\u83B1",
5050
+ BO: "\u73BB\u5229\u7EF4\u4E9A",
5051
+ CD: "\u521A\u679C\uFF08\u91D1\uFF09",
5052
+ CG: "\u521A\u679C\uFF08\u5E03\uFF09",
5053
+ CV: "\u4F5B\u5F97\u89D2",
5054
+ CZ: "\u6377\u514B",
5055
+ DO: "\u591A\u7C73\u5C3C\u52A0",
5056
+ HK: "\u9999\u6E2F",
5057
+ KH: "\u67EC\u57D4\u5BE8",
5058
+ KP: "\u671D\u9C9C",
5059
+ KR: "\u97E9\u56FD",
5060
+ LA: "\u8001\u631D",
5061
+ MD: "\u6469\u5C14\u591A\u74E6",
5062
+ MK: "\u5317\u9A6C\u5176\u987F",
5063
+ MM: "\u7F05\u7538",
5064
+ MO: "\u6FB3\u95E8",
5065
+ NL: "\u8377\u5170",
5066
+ PS: "\u5DF4\u52D2\u65AF\u5766",
5067
+ RU: "\u4FC4\u7F57\u65AF",
5068
+ SY: "\u53D9\u5229\u4E9A",
5069
+ SZ: "\u65AF\u5A01\u58EB\u5170",
5070
+ TL: "\u4E1C\u5E1D\u6C76",
5071
+ TW: "\u53F0\u6E7E",
5072
+ TZ: "\u5766\u6851\u5C3C\u4E9A",
5073
+ VA: "\u68B5\u8482\u5188",
5074
+ VE: "\u59D4\u5185\u745E\u62C9"
5075
+ };
5076
+ var EN_OVERRIDES = {
5077
+ HK: "Hong Kong",
5078
+ MO: "Macau",
5079
+ PS: "Palestine",
5080
+ TW: "Taiwan"
5081
+ };
5082
+ var EXTRA_ALIASES = [
5083
+ "Burma",
5084
+ "Cape Verde",
5085
+ "Czechia",
5086
+ "East Timor",
5087
+ "Holland",
5088
+ "Hong Kong",
5089
+ "Macao",
5090
+ "Macau",
5091
+ "North Korea",
5092
+ "South Korea",
5093
+ "UAE",
5094
+ "UK",
5095
+ "USA",
5096
+ "United Kingdom",
5097
+ "United States"
5098
+ ];
5099
+ function escapeRegExp(value) {
5100
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5101
+ }
5102
+ var displayNameCache = /* @__PURE__ */ new Map();
5103
+ function displayNames(locale) {
5104
+ const cached = displayNameCache.get(locale);
5105
+ if (cached) {
5106
+ return cached;
5107
+ }
5108
+ try {
5109
+ const created = new Intl.DisplayNames([locale], { fallback: "code", type: "region" });
5110
+ displayNameCache.set(locale, created);
5111
+ return created;
5112
+ } catch {
5113
+ return void 0;
5114
+ }
5115
+ }
5116
+ function normalizeRegionCode(code) {
5117
+ const upper = code.trim().toUpperCase();
5118
+ return /^[A-Z]{2}$/.test(upper) ? upper : "";
5119
+ }
5120
+ function regionDisplayName(code, locale = "zh-CN") {
5121
+ const upper = normalizeRegionCode(code);
5122
+ if (upper === "") {
5123
+ return code.trim();
5124
+ }
5125
+ if (locale.toLowerCase().startsWith("zh") && ZH_OVERRIDES[upper] !== void 0) {
5126
+ return ZH_OVERRIDES[upper];
5127
+ }
5128
+ if (locale.toLowerCase().startsWith("en") && EN_OVERRIDES[upper] !== void 0) {
5129
+ return EN_OVERRIDES[upper];
5130
+ }
5131
+ const names = displayNames(locale);
5132
+ return names?.of(upper) ?? upper;
5133
+ }
5134
+ function regionFlag(code) {
5135
+ const upper = normalizeRegionCode(code);
5136
+ if (upper === "") {
5137
+ return "";
5138
+ }
5139
+ const base = 127462;
5140
+ return String.fromCodePoint(base + upper.charCodeAt(0) - 65, base + upper.charCodeAt(1) - 65);
5141
+ }
5142
+ function isRegionCode(code) {
5143
+ return REGION_CODE_SET.has(normalizeRegionCode(code));
5144
+ }
5145
+ var aliasRegex;
5146
+ function buildAliasRegex() {
5147
+ const ascii = [];
5148
+ const other = [];
5149
+ const seen = /* @__PURE__ */ new Set();
5150
+ const add = (alias) => {
5151
+ const trimmed = alias.trim();
5152
+ if (trimmed.length < 2 || seen.has(trimmed)) {
5153
+ return;
5154
+ }
5155
+ seen.add(trimmed);
5156
+ if (/^[\x20-\x7e]+$/.test(trimmed)) {
5157
+ ascii.push(escapeRegExp(trimmed));
5158
+ } else {
5159
+ other.push(escapeRegExp(trimmed));
5160
+ }
5161
+ };
5162
+ for (const code of REGION_CODES) {
5163
+ add(regionDisplayName(code, "zh-CN"));
5164
+ add(regionDisplayName(code, "en"));
5165
+ add(regionFlag(code));
5166
+ }
5167
+ for (const alias of EXTRA_ALIASES) {
5168
+ add(alias);
5169
+ }
5170
+ const parts = [];
5171
+ if (ascii.length > 0) {
5172
+ parts.push(`(?<![A-Za-z])(?:${ascii.join("|")})(?![A-Za-z])`);
5173
+ }
5174
+ if (other.length > 0) {
5175
+ parts.push(`(?:${other.join("|")})`);
5176
+ }
5177
+ return new RegExp(parts.join("|"), "iu");
5178
+ }
5179
+ function getAliasRegex() {
5180
+ aliasRegex ??= buildAliasRegex();
5181
+ return aliasRegex;
5182
+ }
5183
+ function hasRegionName(remark) {
5184
+ const text = remark.trim();
5185
+ if (text === "") {
5186
+ return false;
5187
+ }
5188
+ if (getAliasRegex().test(text)) {
5189
+ return true;
5190
+ }
5191
+ for (const token of text.split(/[^\p{L}\p{N}]+/u)) {
5192
+ if (token === "") {
5193
+ continue;
5194
+ }
5195
+ if (/^\p{Lu}{2}$/u.test(token) && isRegionCode(token)) {
5196
+ return true;
5197
+ }
5198
+ if (/^[A-Za-z]{2}\d{2}/.test(token) && isRegionCode(token.slice(0, 2))) {
5199
+ return true;
5200
+ }
5201
+ }
5202
+ return false;
5203
+ }
5204
+ function formatCountryRemark(remark, code, locale = "zh-CN") {
5205
+ const name = regionDisplayName(code, locale);
5206
+ const trimmed = remark.trim();
5207
+ if (name === "") {
5208
+ return trimmed;
5209
+ }
5210
+ return trimmed === "" ? name : `${name}-${trimmed}`;
5211
+ }
5212
+
4425
5213
  // src/generator/nodes.ts
4426
5214
  function toRegex(pattern) {
4427
5215
  try {
@@ -4630,6 +5418,39 @@ function addEmoji(node, emojiArray) {
4630
5418
  }
4631
5419
  return node.remark;
4632
5420
  }
5421
+ function nodeNameRegex(node, rules) {
5422
+ const original = node.remark;
5423
+ for (const rule of rules) {
5424
+ if (rule.script !== "" || rule.match === "") {
5425
+ continue;
5426
+ }
5427
+ try {
5428
+ node.remark = node.remark.replace(new RegExp(rule.match, "g"), rule.replace);
5429
+ } catch {
5430
+ }
5431
+ }
5432
+ if (node.remark === "") {
5433
+ node.remark = original;
5434
+ }
5435
+ }
5436
+ var NAME_SEPARATOR_EDGES = /^[-_.\s]+|[-_.\s]+$/g;
5437
+ var NAME_SEPARATOR_RUN = /([-_.\s])[-_.\s]+/g;
5438
+ function nodeNameRemove(node, removeList) {
5439
+ const original = node.remark;
5440
+ let remark = node.remark;
5441
+ let removed = false;
5442
+ for (const fragment of removeList) {
5443
+ if (fragment === "" || !remark.includes(fragment)) {
5444
+ continue;
5445
+ }
5446
+ remark = remark.split(fragment).join("");
5447
+ removed = true;
5448
+ }
5449
+ if (removed) {
5450
+ remark = remark.replace(NAME_SEPARATOR_EDGES, "").replace(NAME_SEPARATOR_RUN, "$1");
5451
+ }
5452
+ node.remark = remark === "" ? original : remark;
5453
+ }
4633
5454
  function nodeRename(node, renameArray) {
4634
5455
  const original = node.remark;
4635
5456
  for (const rule of renameArray) {
@@ -4645,15 +5466,39 @@ function nodeRename(node, renameArray) {
4645
5466
  node.remark = original;
4646
5467
  }
4647
5468
  }
5469
+ function addCountryPrefix(node, resolve, locale = "zh-CN") {
5470
+ const remark = node.remark.trim();
5471
+ if (hasRegionName(remark)) {
5472
+ return node.remark;
5473
+ }
5474
+ const code = resolve(node);
5475
+ if (code === void 0 || code === "") {
5476
+ return node.remark;
5477
+ }
5478
+ return formatCountryRemark(remark, code, locale);
5479
+ }
4648
5480
  function preprocessNodes(nodes, options) {
4649
5481
  for (const node of nodes) {
4650
5482
  if (options.removeEmoji) {
4651
5483
  node.remark = removeEmoji(node.remark).trim();
4652
5484
  }
5485
+ if (options.nameRegexArray && options.nameRegexArray.length > 0) {
5486
+ nodeNameRegex(node, options.nameRegexArray);
5487
+ }
5488
+ if (options.nameRemove && options.nameRemove.length > 0) {
5489
+ nodeNameRemove(node, options.nameRemove);
5490
+ }
4653
5491
  nodeRename(node, options.renameArray);
4654
5492
  if (options.addEmoji) {
4655
5493
  node.remark = addEmoji(node, options.emojiArray);
4656
5494
  }
5495
+ if (options.autoCountry && options.countryResolver) {
5496
+ node.remark = addCountryPrefix(
5497
+ node,
5498
+ options.countryResolver,
5499
+ options.countryLocale ?? "zh-CN"
5500
+ );
5501
+ }
4657
5502
  }
4658
5503
  if (options.sortFlag) {
4659
5504
  nodes.sort((a, b) => a.remark < b.remark ? -1 : a.remark > b.remark ? 1 : 0);
@@ -5063,6 +5908,14 @@ function buildClashProxy(x, settings, clashR, remark, skipCertVerify, tfo) {
5063
5908
  if (x.fingerprint !== "") {
5064
5909
  proxy["client-fingerprint"] = x.fingerprint;
5065
5910
  }
5911
+ if (x.publicKey !== "" || x.shortId !== "") {
5912
+ const realityOpts = {};
5913
+ if (x.publicKey !== "") {
5914
+ realityOpts["public-key"] = x.publicKey;
5915
+ }
5916
+ realityOpts["short-id"] = x.shortId;
5917
+ proxy["reality-opts"] = realityOpts;
5918
+ }
5066
5919
  if (x.alpn.length > 0) {
5067
5920
  proxy.alpn = x.alpn;
5068
5921
  }
@@ -5146,60 +5999,60 @@ function buildClash(nodes, extraProxyGroup, settings, clashR = false) {
5146
5999
  return { doc: { proxies }, nodeList };
5147
6000
  }
5148
6001
  const groups = [];
5149
- for (const group of extraProxyGroup) {
6002
+ for (const group2 of extraProxyGroup) {
5150
6003
  const singlegroup = {
5151
- name: group.name,
5152
- type: group.type === "smart" ? "url-test" : group.type
6004
+ name: group2.name,
6005
+ type: group2.type === "smart" ? "url-test" : group2.type
5153
6006
  };
5154
- switch (group.type) {
6007
+ switch (group2.type) {
5155
6008
  case "select":
5156
6009
  case "relay":
5157
6010
  break;
5158
6011
  case "load-balance":
5159
- singlegroup.strategy = group.strategy;
5160
- singlegroup.lazy = group.lazy;
5161
- singlegroup.url = group.url;
5162
- if (group.interval > 0) {
5163
- singlegroup.interval = group.interval;
6012
+ singlegroup.strategy = group2.strategy;
6013
+ singlegroup.lazy = group2.lazy;
6014
+ singlegroup.url = group2.url;
6015
+ if (group2.interval > 0) {
6016
+ singlegroup.interval = group2.interval;
5164
6017
  }
5165
- if (group.tolerance > 0) {
5166
- singlegroup.tolerance = group.tolerance;
6018
+ if (group2.tolerance > 0) {
6019
+ singlegroup.tolerance = group2.tolerance;
5167
6020
  }
5168
6021
  break;
5169
6022
  case "smart":
5170
6023
  case "url-test":
5171
- if (group.lazy !== void 0) {
5172
- singlegroup.lazy = group.lazy;
6024
+ if (group2.lazy !== void 0) {
6025
+ singlegroup.lazy = group2.lazy;
5173
6026
  }
5174
- singlegroup.url = group.url;
5175
- if (group.interval > 0) {
5176
- singlegroup.interval = group.interval;
6027
+ singlegroup.url = group2.url;
6028
+ if (group2.interval > 0) {
6029
+ singlegroup.interval = group2.interval;
5177
6030
  }
5178
- if (group.tolerance > 0) {
5179
- singlegroup.tolerance = group.tolerance;
6031
+ if (group2.tolerance > 0) {
6032
+ singlegroup.tolerance = group2.tolerance;
5180
6033
  }
5181
6034
  break;
5182
6035
  case "fallback":
5183
- singlegroup.url = group.url;
5184
- if (group.interval > 0) {
5185
- singlegroup.interval = group.interval;
6036
+ singlegroup.url = group2.url;
6037
+ if (group2.interval > 0) {
6038
+ singlegroup.interval = group2.interval;
5186
6039
  }
5187
- if (group.tolerance > 0) {
5188
- singlegroup.tolerance = group.tolerance;
6040
+ if (group2.tolerance > 0) {
6041
+ singlegroup.tolerance = group2.tolerance;
5189
6042
  }
5190
6043
  break;
5191
6044
  default:
5192
6045
  continue;
5193
6046
  }
5194
- if (group.disableUdp !== void 0) {
5195
- singlegroup["disable-udp"] = group.disableUdp;
6047
+ if (group2.disableUdp !== void 0) {
6048
+ singlegroup["disable-udp"] = group2.disableUdp;
5196
6049
  }
5197
6050
  const filtered = [];
5198
- for (const rule of group.proxies) {
6051
+ for (const rule of group2.proxies) {
5199
6052
  groupGenerate(rule, nodeList, filtered, true);
5200
6053
  }
5201
- if (group.usingProvider.length > 0) {
5202
- singlegroup.use = group.usingProvider;
6054
+ if (group2.usingProvider.length > 0) {
6055
+ singlegroup.use = group2.usingProvider;
5203
6056
  } else {
5204
6057
  if (filtered.length === 0) {
5205
6058
  filtered.push("DIRECT");
@@ -5233,6 +6086,162 @@ function toClash(nodes, extraProxyGroup, settings, clashR = false) {
5233
6086
  return stringifyClash(doc);
5234
6087
  }
5235
6088
 
6089
+ // src/generator/default-profile.ts
6090
+ var TEST_URL = "http://www.gstatic.com/generate_204";
6091
+ function group(name, type, proxies, extra = {}) {
6092
+ return {
6093
+ disableUdp: void 0,
6094
+ evaluateBeforeUse: void 0,
6095
+ interval: 0,
6096
+ lazy: void 0,
6097
+ name,
6098
+ persistent: void 0,
6099
+ proxies: [...proxies],
6100
+ strategy: "consistent-hashing" /* ConsistentHashing */,
6101
+ timeout: 5,
6102
+ tolerance: 0,
6103
+ type,
6104
+ url: "",
6105
+ usingProvider: [],
6106
+ ...extra
6107
+ };
6108
+ }
6109
+ function getDefaultProxyGroups() {
6110
+ return [
6111
+ group("\u{1F680} \u8282\u70B9\u9009\u62E9", "select" /* Select */, ["[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9", "[]DIRECT", ".*"]),
6112
+ group("\u267B\uFE0F \u81EA\u52A8\u9009\u62E9", "url-test" /* URLTest */, [".*"], {
6113
+ interval: 300,
6114
+ tolerance: 50,
6115
+ url: TEST_URL
6116
+ }),
6117
+ group("\u{1F30D} \u56FD\u5916\u5A92\u4F53", "select" /* Select */, [
6118
+ "[]\u{1F680} \u8282\u70B9\u9009\u62E9",
6119
+ "[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9",
6120
+ "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE",
6121
+ ".*"
6122
+ ]),
6123
+ group("\u{1F4F2} \u7535\u62A5\u4FE1\u606F", "select" /* Select */, ["[]\u{1F680} \u8282\u70B9\u9009\u62E9", "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE", ".*"]),
6124
+ group("\u24C2\uFE0F \u5FAE\u8F6F\u670D\u52A1", "select" /* Select */, ["[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "[]\u{1F680} \u8282\u70B9\u9009\u62E9", ".*"]),
6125
+ group("\u{1F34E} \u82F9\u679C\u670D\u52A1", "select" /* Select */, ["[]\u{1F680} \u8282\u70B9\u9009\u62E9", "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE", ".*"]),
6126
+ group("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "select" /* Select */, ["[]DIRECT", "[]\u{1F680} \u8282\u70B9\u9009\u62E9", "[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9"]),
6127
+ group("\u{1F6D1} \u5168\u7403\u62E6\u622A", "select" /* Select */, ["[]REJECT", "[]DIRECT"]),
6128
+ group("\u{1F343} \u5E94\u7528\u51C0\u5316", "select" /* Select */, ["[]REJECT", "[]DIRECT"]),
6129
+ group("\u{1F41F} \u6F0F\u7F51\u4E4B\u9C7C", "select" /* Select */, [
6130
+ "[]\u{1F680} \u8282\u70B9\u9009\u62E9",
6131
+ "[]\u{1F3AF} \u5168\u7403\u76F4\u8FDE",
6132
+ "[]\u267B\uFE0F \u81EA\u52A8\u9009\u62E9",
6133
+ ".*"
6134
+ ])
6135
+ ];
6136
+ }
6137
+ function inline(group2, rule) {
6138
+ return {
6139
+ content: `[]${rule}`,
6140
+ ruleGroup: group2,
6141
+ rulePath: "",
6142
+ rulePathTyped: "",
6143
+ ruleType: "surge" /* Surge */,
6144
+ updateInterval: 86400
6145
+ };
6146
+ }
6147
+ function surge(group2, lines) {
6148
+ return {
6149
+ content: lines.join("\n"),
6150
+ ruleGroup: group2,
6151
+ rulePath: "",
6152
+ rulePathTyped: "",
6153
+ ruleType: "surge" /* Surge */,
6154
+ updateInterval: 86400
6155
+ };
6156
+ }
6157
+ function getDefaultRulesets() {
6158
+ return [
6159
+ surge("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", [
6160
+ "DOMAIN-SUFFIX,localhost",
6161
+ "DOMAIN,localhost",
6162
+ "IP-CIDR,127.0.0.0/8",
6163
+ "IP-CIDR,10.0.0.0/8",
6164
+ "IP-CIDR,172.16.0.0/12",
6165
+ "IP-CIDR,192.168.0.0/16",
6166
+ "IP-CIDR,224.0.0.0/4",
6167
+ "DOMAIN-SUFFIX,cn",
6168
+ "DOMAIN-SUFFIX,126.com",
6169
+ "DOMAIN-SUFFIX,163.com",
6170
+ "DOMAIN-SUFFIX,qq.com",
6171
+ "DOMAIN-SUFFIX,baidu.com",
6172
+ "DOMAIN-SUFFIX,alibaba.com",
6173
+ "DOMAIN-SUFFIX,taobao.com"
6174
+ ]),
6175
+ surge("\u{1F6D1} \u5168\u7403\u62E6\u622A", [
6176
+ "DOMAIN-SUFFIX,doubleclick.net",
6177
+ "DOMAIN-SUFFIX,googlesyndication.com",
6178
+ "DOMAIN-SUFFIX,googleadservices.com",
6179
+ "DOMAIN-SUFFIX,ads.yahoo.com",
6180
+ "DOMAIN-KEYWORD,adservice",
6181
+ "DOMAIN-KEYWORD,advertising"
6182
+ ]),
6183
+ surge("\u{1F343} \u5E94\u7528\u51C0\u5316", [
6184
+ "DOMAIN-SUFFIX,app-measurement.com",
6185
+ "DOMAIN-SUFFIX,crashlytics.com",
6186
+ "DOMAIN-KEYWORD,analytics",
6187
+ "DOMAIN-KEYWORD,tracking"
6188
+ ]),
6189
+ surge("\u{1F30D} \u56FD\u5916\u5A92\u4F53", [
6190
+ "DOMAIN-SUFFIX,netflix.com",
6191
+ "DOMAIN-SUFFIX,nflxvideo.net",
6192
+ "DOMAIN-SUFFIX,youtube.com",
6193
+ "DOMAIN-SUFFIX,googlevideo.com",
6194
+ "DOMAIN-SUFFIX,disneyplus.com",
6195
+ "DOMAIN-SUFFIX,spotify.com",
6196
+ "DOMAIN-SUFFIX,tiktok.com"
6197
+ ]),
6198
+ surge("\u{1F4F2} \u7535\u62A5\u4FE1\u606F", [
6199
+ "DOMAIN-SUFFIX,telegram.org",
6200
+ "DOMAIN-SUFFIX,t.me",
6201
+ "DOMAIN-SUFFIX,telegram.me",
6202
+ "IP-CIDR,149.154.160.0/20",
6203
+ "IP-CIDR,91.108.4.0/22"
6204
+ ]),
6205
+ surge("\u24C2\uFE0F \u5FAE\u8F6F\u670D\u52A1", [
6206
+ "DOMAIN-SUFFIX,microsoft.com",
6207
+ "DOMAIN-SUFFIX,windows.com",
6208
+ "DOMAIN-SUFFIX,office.com",
6209
+ "DOMAIN-SUFFIX,live.com"
6210
+ ]),
6211
+ surge("\u{1F34E} \u82F9\u679C\u670D\u52A1", [
6212
+ "DOMAIN-SUFFIX,apple.com",
6213
+ "DOMAIN-SUFFIX,icloud.com",
6214
+ "DOMAIN-SUFFIX,apple-cloudkit.com"
6215
+ ]),
6216
+ surge("\u{1F680} \u8282\u70B9\u9009\u62E9", ["DOMAIN-SUFFIX,google.com", "DOMAIN-SUFFIX,github.com"]),
6217
+ inline("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "GEOIP,CN"),
6218
+ inline("\u{1F41F} \u6F0F\u7F51\u4E4B\u9C7C", "FINAL")
6219
+ ];
6220
+ }
6221
+ function getDefaultRulesetConfigs() {
6222
+ const base = "https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash";
6223
+ const list = (group2, file) => ({
6224
+ group: group2,
6225
+ interval: 86400,
6226
+ url: `${base}/${file}`
6227
+ });
6228
+ return [
6229
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "LocalAreaNetwork.list"),
6230
+ list("\u{1F6D1} \u5168\u7403\u62E6\u622A", "BanAD.list"),
6231
+ list("\u{1F343} \u5E94\u7528\u51C0\u5316", "BanProgramAD.list"),
6232
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "GoogleCN.list"),
6233
+ list("\u24C2\uFE0F \u5FAE\u8F6F\u670D\u52A1", "Microsoft.list"),
6234
+ list("\u{1F34E} \u82F9\u679C\u670D\u52A1", "Apple.list"),
6235
+ list("\u{1F30D} \u56FD\u5916\u5A92\u4F53", "ProxyMedia.list"),
6236
+ list("\u{1F4F2} \u7535\u62A5\u4FE1\u606F", "Telegram.list"),
6237
+ list("\u{1F680} \u8282\u70B9\u9009\u62E9", "ProxyLite.list"),
6238
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "ChinaDomain.list"),
6239
+ list("\u{1F3AF} \u5168\u7403\u76F4\u8FDE", "ChinaCompanyIp.list"),
6240
+ { group: "\u{1F3AF} \u5168\u7403\u76F4\u8FDE", interval: 86400, url: "[]GEOIP,CN" },
6241
+ { group: "\u{1F41F} \u6F0F\u7F51\u4E4B\u9C7C", interval: 86400, url: "[]FINAL" }
6242
+ ];
6243
+ }
6244
+
5236
6245
  // src/generator/links.ts
5237
6246
  function vmessLinkConstruct(remarks, add, port, type, id, aid, net, path, host, tls) {
5238
6247
  return JSON.stringify({
@@ -5258,8 +6267,8 @@ function ssLinkConstruct(proxy) {
5258
6267
  link += `#${urlEncode(proxy.remark)}`;
5259
6268
  return link;
5260
6269
  }
5261
- function ssrLinkConstruct(hostname, port, protocol, method, obfs, password, group, remark, obfsParam = "", protocolParam = "") {
5262
- const payload = `${hostname}:${port}:${protocol}:${method}:${obfs}:${urlSafeBase64Encode(password)}/?group=${urlSafeBase64Encode(group)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(obfsParam)}&protoparam=${urlSafeBase64Encode(protocolParam)}`;
6270
+ function ssrLinkConstruct(hostname, port, protocol, method, obfs, password, group2, remark, obfsParam = "", protocolParam = "") {
6271
+ const payload = `${hostname}:${port}:${protocol}:${method}:${obfs}:${urlSafeBase64Encode(password)}/?group=${urlSafeBase64Encode(group2)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(obfsParam)}&protoparam=${urlSafeBase64Encode(protocolParam)}`;
5263
6272
  return `ssr://${urlSafeBase64Encode(payload)}`;
5264
6273
  }
5265
6274
  function trojanLinkConstruct(proxy) {
@@ -5406,8 +6415,8 @@ function proxyToSsSub(nodes, baseConf) {
5406
6415
  }
5407
6416
  return JSON.stringify(proxies);
5408
6417
  }
5409
- function proxyToSsd(nodes, group, userinfo) {
5410
- const resolvedGroup = group === "" ? "SSD" : group;
6418
+ function proxyToSsd(nodes, group2, userinfo) {
6419
+ const resolvedGroup = group2 === "" ? "SSD" : group2;
5411
6420
  const doc = {
5412
6421
  airport: resolvedGroup,
5413
6422
  encryption: "aes-128-gcm",
@@ -5420,7 +6429,7 @@ function proxyToSsd(nodes, group, userinfo) {
5420
6429
  const download = getUrlArg(data, "download");
5421
6430
  const total = getUrlArg(data, "total");
5422
6431
  const expiry = getUrlArg(data, "expire");
5423
- const gb = 1024 ** 3;
6432
+ const gb = 1e3 ** 3;
5424
6433
  doc.traffic_used = (Number(upload || 0) + Number(download || 0)) / gb;
5425
6434
  doc.traffic_total = Number(total || 0) / gb;
5426
6435
  if (expiry !== "") {
@@ -5659,6 +6668,14 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5659
6668
  if (x.host !== "") {
5660
6669
  proxy += `, sni=${x.host}`;
5661
6670
  }
6671
+ if (x.transferProtocol === "ws") {
6672
+ proxy += ", ws=true";
6673
+ if (x.path !== "") {
6674
+ proxy += `, ws-path=${x.path}`;
6675
+ }
6676
+ const wsHost = x.host !== "" ? x.host : x.hostname;
6677
+ proxy += `, ws-headers=Host:"${wsHost}"`;
6678
+ }
5662
6679
  if (scv !== void 0) {
5663
6680
  proxy += `, skip-cert-verify=${triStr(scv)}`;
5664
6681
  }
@@ -5704,7 +6721,7 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5704
6721
  if (surgeVer < 4) {
5705
6722
  continue;
5706
6723
  }
5707
- proxy = `hysteria, ${x.hostname}, ${x.port}, password=${x.password}`;
6724
+ proxy = `hysteria2, ${x.hostname}, ${x.port}, password=${x.password}`;
5708
6725
  if (x.downSpeed) {
5709
6726
  proxy += `, download-bandwidth=${x.downSpeed}`;
5710
6727
  }
@@ -5747,10 +6764,10 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5747
6764
  return { extraSections, groups: [], proxies, sectionOrder };
5748
6765
  }
5749
6766
  const groups = [];
5750
- for (const group of extraProxyGroup) {
6767
+ for (const group2 of extraProxyGroup) {
5751
6768
  const filtered = [];
5752
6769
  let groupLine = "";
5753
- switch (group.type) {
6770
+ switch (group2.type) {
5754
6771
  case "select":
5755
6772
  case "smart":
5756
6773
  case "url-test":
@@ -5762,15 +6779,15 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5762
6779
  }
5763
6780
  break;
5764
6781
  case "ssid": {
5765
- const rest = group.proxies.slice(1).join(",");
5766
- const line = `${group.type},default=${group.proxies[0] ?? ""},${rest}`;
5767
- groups.push(`${group.name} = ${line}`);
6782
+ const rest = group2.proxies.slice(1).join(",");
6783
+ const line = `${group2.type},default=${group2.proxies[0] ?? ""},${rest}`;
6784
+ groups.push(`${group2.name} = ${line}`);
5768
6785
  continue;
5769
6786
  }
5770
6787
  default:
5771
6788
  continue;
5772
6789
  }
5773
- for (const rule of group.proxies) {
6790
+ for (const rule of group2.proxies) {
5774
6791
  groupGenerate(rule, nodelist, filtered, true);
5775
6792
  }
5776
6793
  if (filtered.length === 0) {
@@ -5779,27 +6796,27 @@ function buildSurge(nodes, extraProxyGroup, settings, options) {
5779
6796
  if (filtered.length === 1) {
5780
6797
  const single = filtered[0].toLowerCase();
5781
6798
  if (single === "direct" || single === "reject" || single === "reject-tinygif") {
5782
- proxies.push(`${group.name} = ${single}`);
6799
+ proxies.push(`${group2.name} = ${single}`);
5783
6800
  continue;
5784
6801
  }
5785
6802
  }
5786
- groupLine = `${group.type},${filtered.join(",")}`;
5787
- if (group.type === "url-test" || group.type === "fallback" || group.type === "load-balance") {
5788
- groupLine += `,url=${group.url},interval=${group.interval}`;
5789
- if (group.tolerance > 0) {
5790
- groupLine += `,tolerance=${group.tolerance}`;
6803
+ groupLine = `${group2.type},${filtered.join(",")}`;
6804
+ if (group2.type === "url-test" || group2.type === "fallback" || group2.type === "load-balance") {
6805
+ groupLine += `,url=${group2.url},interval=${group2.interval}`;
6806
+ if (group2.tolerance > 0) {
6807
+ groupLine += `,tolerance=${group2.tolerance}`;
5791
6808
  }
5792
- if (group.timeout > 0) {
5793
- groupLine += `,timeout=${group.timeout}`;
6809
+ if (group2.timeout > 0) {
6810
+ groupLine += `,timeout=${group2.timeout}`;
5794
6811
  }
5795
- if (group.persistent !== void 0) {
5796
- groupLine += `,persistent=${triStr(group.persistent)}`;
6812
+ if (group2.persistent !== void 0) {
6813
+ groupLine += `,persistent=${triStr(group2.persistent)}`;
5797
6814
  }
5798
- if (group.evaluateBeforeUse !== void 0) {
5799
- groupLine += `,evaluate-before-use=${triStr(group.evaluateBeforeUse)}`;
6815
+ if (group2.evaluateBeforeUse !== void 0) {
6816
+ groupLine += `,evaluate-before-use=${triStr(group2.evaluateBeforeUse)}`;
5800
6817
  }
5801
6818
  }
5802
- groups.push(`${group.name} = ${groupLine}`);
6819
+ groups.push(`${group2.name} = ${groupLine}`);
5803
6820
  }
5804
6821
  return { extraSections, groups, proxies, sectionOrder };
5805
6822
  }
@@ -5899,11 +6916,47 @@ function buildLoonNode(x, settings, remark) {
5899
6916
  proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5900
6917
  }
5901
6918
  break;
6919
+ case "vless" /* VLESS */:
6920
+ if (x.transferProtocol !== "" && x.transferProtocol !== "tcp" && x.transferProtocol !== "ws") {
6921
+ return void 0;
6922
+ }
6923
+ proxy = `VLESS,${hostname},${port},"${x.userId}"`;
6924
+ if (x.flow !== "") {
6925
+ proxy += `,flow=${x.flow}`;
6926
+ }
6927
+ if (x.publicKey !== "") {
6928
+ proxy += `,public-key="${x.publicKey}"`;
6929
+ }
6930
+ if (x.shortId !== "") {
6931
+ proxy += `,short-id=${x.shortId}`;
6932
+ }
6933
+ if (x.transferProtocol === "ws") {
6934
+ proxy += `,path=${x.path},host=${x.host}`;
6935
+ }
6936
+ proxy += `,transport=${x.transferProtocol === "ws" ? "ws" : "tcp"}`;
6937
+ if (x.udp !== void 0) {
6938
+ proxy += `,udp=${x.udp ? "true" : "false"}`;
6939
+ } else {
6940
+ proxy += ",udp=false";
6941
+ }
6942
+ proxy += `,over-tls=${x.tlsSecure ? "true" : "false"}`;
6943
+ if (x.tlsSecure && x.serverName !== "") {
6944
+ proxy += `,sni=${x.serverName}`;
6945
+ }
6946
+ if (scv !== void 0) {
6947
+ proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
6948
+ }
6949
+ break;
5902
6950
  case "trojan" /* Trojan */:
5903
6951
  proxy = `trojan,${hostname},${port},"${x.password}"`;
5904
6952
  if (x.host !== "") {
5905
6953
  proxy += `,tls-name=${x.host}`;
5906
6954
  }
6955
+ if (x.transferProtocol === "ws") {
6956
+ proxy += `,transport=ws,path=${x.path},host=${x.host}`;
6957
+ } else {
6958
+ proxy += ",transport=tcp";
6959
+ }
5907
6960
  if (scv !== void 0) {
5908
6961
  proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5909
6962
  }
@@ -5945,26 +6998,22 @@ function buildLoonNode(x, settings, remark) {
5945
6998
  proxy += `, peers=[{${generatePeer(x, true)}}]`;
5946
6999
  break;
5947
7000
  case "hysteria2" /* Hysteria2 */:
5948
- proxy = `hysteria2,${hostname},${port},"${x.password}"`;
5949
- if (scv !== void 0) {
5950
- proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5951
- }
5952
- if (x.fingerprint !== "") {
5953
- proxy += `,tls-cert-sha256=${x.fingerprint}`;
5954
- }
7001
+ proxy = `Hysteria2,${hostname},${port},"${x.password}"`;
5955
7002
  if (x.sni !== "") {
5956
7003
  proxy += `,sni=${x.sni}`;
5957
7004
  }
5958
- break;
5959
- case "anytls" /* AnyTLS */:
5960
- proxy = `anytls,${hostname},${port},"${x.password}"`;
5961
- if (x.sni !== "") {
5962
- proxy += `,sni=${x.sni}`;
7005
+ if (x.downSpeed > 0) {
7006
+ proxy += `,download-bandwidth=${x.downSpeed}`;
5963
7007
  }
5964
7008
  if (scv !== void 0) {
5965
7009
  proxy += `,skip-cert-verify=${scv ? "true" : "false"}`;
5966
7010
  }
7011
+ if (x.fingerprint !== "") {
7012
+ proxy += `,tls-cert-sha256=${x.fingerprint}`;
7013
+ }
5967
7014
  break;
7015
+ // Loon has no AnyTLS/TUIC mapping — keep the drop instead of
7016
+ // emitting non-standard lines.
5968
7017
  default:
5969
7018
  return void 0;
5970
7019
  }
@@ -5979,48 +7028,48 @@ function buildLoonNode(x, settings, remark) {
5979
7028
  }
5980
7029
  function buildLoonGroups(extraProxyGroup, nodelist) {
5981
7030
  const lines = [];
5982
- for (const group of extraProxyGroup) {
7031
+ for (const group2 of extraProxyGroup) {
5983
7032
  const filtered = [];
5984
- switch (group.type) {
7033
+ switch (group2.type) {
5985
7034
  case "select":
5986
7035
  case "load-balance":
5987
7036
  case "url-test":
5988
7037
  case "fallback":
5989
7038
  break;
5990
7039
  case "ssid":
5991
- if (group.proxies.length < 2) {
7040
+ if (group2.proxies.length < 2) {
5992
7041
  continue;
5993
7042
  }
5994
7043
  lines.push(
5995
- `${group.name} = ssid,default=${group.proxies[0]},${group.proxies.slice(1).join(",")}`
7044
+ `${group2.name} = ssid,default=${group2.proxies[0]},${group2.proxies.slice(1).join(",")}`
5996
7045
  );
5997
7046
  continue;
5998
7047
  default:
5999
7048
  continue;
6000
7049
  }
6001
- for (const rule of group.proxies) {
7050
+ for (const rule of group2.proxies) {
6002
7051
  groupGenerate(rule, nodelist, filtered, true);
6003
7052
  }
6004
7053
  if (filtered.length === 0) {
6005
7054
  filtered.push("DIRECT");
6006
7055
  }
6007
- let groupLine = `${group.type},${filtered.join(",")}`;
6008
- if (group.type !== "select") {
6009
- groupLine += `,url=${group.url},interval=${group.interval}`;
6010
- if (group.type === "load-balance") {
6011
- groupLine += `,algorithm=${group.strategy === "round-robin" ? "round-robin" : "pcc"}`;
6012
- if (group.timeout > 0) {
6013
- groupLine += `,max-timeout=${group.timeout}`;
7056
+ let groupLine = `${group2.type},${filtered.join(",")}`;
7057
+ if (group2.type !== "select") {
7058
+ groupLine += `,url=${group2.url},interval=${group2.interval}`;
7059
+ if (group2.type === "load-balance") {
7060
+ groupLine += `,algorithm=${group2.strategy === "round-robin" ? "round-robin" : "pcc"}`;
7061
+ if (group2.timeout > 0) {
7062
+ groupLine += `,max-timeout=${group2.timeout}`;
6014
7063
  }
6015
7064
  }
6016
- if (group.type === "url-test" && group.tolerance > 0) {
6017
- groupLine += `,tolerance=${group.tolerance}`;
7065
+ if (group2.type === "url-test" && group2.tolerance > 0) {
7066
+ groupLine += `,tolerance=${group2.tolerance}`;
6018
7067
  }
6019
- if (group.type === "fallback") {
6020
- groupLine += `,max-timeout=${group.timeout}`;
7068
+ if (group2.type === "fallback") {
7069
+ groupLine += `,max-timeout=${group2.timeout}`;
6021
7070
  }
6022
7071
  }
6023
- lines.push(`${group.name} = ${groupLine}`);
7072
+ lines.push(`${group2.name} = ${groupLine}`);
6024
7073
  }
6025
7074
  return lines;
6026
7075
  }
@@ -6051,6 +7100,9 @@ function toLoon(nodes, extraProxyGroup, settings, options) {
6051
7100
  if (groupLines.length > 0) {
6052
7101
  setRawSection(sections, "Proxy Group", groupLines);
6053
7102
  }
7103
+ if (options.rules && options.rules.lines.length > 0) {
7104
+ setRawSection(sections, options.rules.section, options.rules.lines);
7105
+ }
6054
7106
  return renderRawIni(sections);
6055
7107
  }
6056
7108
 
@@ -6181,12 +7233,12 @@ function convertRuleset(content, type) {
6181
7233
  }
6182
7234
  return lines.join("\n");
6183
7235
  }
6184
- function transformRuleToCommon(input, group, noResolveOnly = false) {
7236
+ function transformRuleToCommon(input, group2, noResolveOnly = false) {
6185
7237
  const parts = input.split(",");
6186
7238
  if (parts.length < 2) {
6187
- return `${parts[0] ?? ""},${group}`;
7239
+ return `${parts[0] ?? ""},${group2}`;
6188
7240
  }
6189
- let line = `${parts[0]},${parts[1]},${group}`;
7241
+ let line = `${parts[0]},${parts[1]},${group2}`;
6190
7242
  if (parts.length > 2 && (!noResolveOnly || parts[2] === "no-resolve")) {
6191
7243
  line += `,${parts[2]}`;
6192
7244
  }
@@ -6195,7 +7247,7 @@ function transformRuleToCommon(input, group, noResolveOnly = false) {
6195
7247
  function startsWithAny(line, types) {
6196
7248
  return types.some((type) => line.startsWith(type));
6197
7249
  }
6198
- function collectRuleLines(content, group, noResolveOnly) {
7250
+ function collectRuleLines(content, group2, noResolveOnly) {
6199
7251
  const converted = convertRuleset(content, "surge" /* Surge */);
6200
7252
  const out = [];
6201
7253
  for (const rawLine of converted.split(/\r?\n/)) {
@@ -6209,7 +7261,7 @@ function collectRuleLines(content, group, noResolveOnly) {
6209
7261
  if (line === "") {
6210
7262
  continue;
6211
7263
  }
6212
- out.push(transformRuleToCommon(line, group, noResolveOnly));
7264
+ out.push(transformRuleToCommon(line, group2, noResolveOnly));
6213
7265
  }
6214
7266
  return out;
6215
7267
  }
@@ -6220,13 +7272,13 @@ function rulesetToClashRules(rulesets) {
6220
7272
  if (content === "") {
6221
7273
  continue;
6222
7274
  }
6223
- const group = ruleset.ruleGroup;
7275
+ const group2 = ruleset.ruleGroup;
6224
7276
  if (content.startsWith("[]")) {
6225
7277
  let line = content.slice(2);
6226
7278
  if (line.startsWith("FINAL")) {
6227
7279
  line = `MATCH${line.slice(5)}`;
6228
7280
  }
6229
- rules.push(transformRuleToCommon(line, group));
7281
+ rules.push(transformRuleToCommon(line, group2));
6230
7282
  continue;
6231
7283
  }
6232
7284
  const converted = convertRuleset(content, ruleset.ruleType);
@@ -6241,7 +7293,7 @@ function rulesetToClashRules(rulesets) {
6241
7293
  if (line.includes("//")) {
6242
7294
  line = stripComment(line);
6243
7295
  }
6244
- rules.push(transformRuleToCommon(line, group));
7296
+ rules.push(transformRuleToCommon(line, group2));
6245
7297
  }
6246
7298
  }
6247
7299
  return rules;
@@ -6283,21 +7335,21 @@ function rulesetToSurgeRules(rulesets, surgeVersion) {
6283
7335
  if (ruleset.content === "") {
6284
7336
  continue;
6285
7337
  }
6286
- const group = ruleset.ruleGroup;
7338
+ const group2 = ruleset.ruleGroup;
6287
7339
  if (ruleset.content.startsWith("[]")) {
6288
7340
  let line = ruleset.content.slice(2);
6289
7341
  if (line === "MATCH") {
6290
7342
  line = "FINAL";
6291
7343
  }
6292
7344
  if (surgeVersion === -1 || surgeVersion === -2) {
6293
- line = transformRuleToCommon(line, group, true);
7345
+ line = transformRuleToCommon(line, group2, true);
6294
7346
  } else if (!line.startsWith("AND") && !line.startsWith("OR") && !line.startsWith("NOT")) {
6295
- line = transformRuleToCommon(line, group);
7347
+ line = transformRuleToCommon(line, group2);
6296
7348
  }
6297
7349
  lines.push(line.replace(/,,/g, ","));
6298
7350
  continue;
6299
7351
  }
6300
- for (const rawLine of collectRuleLines(ruleset.content, group, noResolveOnly)) {
7352
+ for (const rawLine of collectRuleLines(ruleset.content, group2, noResolveOnly)) {
6301
7353
  if (surgeVersion === -2 && rawLine.startsWith("IP-CIDR6")) {
6302
7354
  continue;
6303
7355
  }
@@ -6340,11 +7392,11 @@ function rulesetToSingBoxRules(rulesets) {
6340
7392
  if (content === "") {
6341
7393
  continue;
6342
7394
  }
6343
- const group = ruleset.ruleGroup;
7395
+ const group2 = ruleset.ruleGroup;
6344
7396
  if (content.startsWith("[]")) {
6345
7397
  const line = content.slice(2);
6346
7398
  if (line.startsWith("FINAL") || line.startsWith("MATCH")) {
6347
- final = group;
7399
+ final = group2;
6348
7400
  continue;
6349
7401
  }
6350
7402
  const parts = line.split(",");
@@ -6356,7 +7408,7 @@ function rulesetToSingBoxRules(rulesets) {
6356
7408
  if (type === "match" || type === "final") {
6357
7409
  rules.push({ outbound: value });
6358
7410
  } else {
6359
- rules.push({ outbound: group, [type]: value });
7411
+ rules.push({ outbound: group2, [type]: value });
6360
7412
  }
6361
7413
  continue;
6362
7414
  }
@@ -6387,7 +7439,7 @@ function rulesetToSingBoxRules(rulesets) {
6387
7439
  if (Object.keys(rule).length === 0) {
6388
7440
  continue;
6389
7441
  }
6390
- rule.outbound = group;
7442
+ rule.outbound = group2;
6391
7443
  rules.push(rule);
6392
7444
  }
6393
7445
  return { final, rules };
@@ -6448,8 +7500,8 @@ function buildMellowEndpoint(x, remark) {
6448
7500
  }
6449
7501
  function buildMellowGroups(extraProxyGroup, nodelist, remarks) {
6450
7502
  const lines = [];
6451
- for (const group of extraProxyGroup) {
6452
- switch (group.type) {
7503
+ for (const group2 of extraProxyGroup) {
7504
+ switch (group2.type) {
6453
7505
  case "select":
6454
7506
  case "url-test":
6455
7507
  case "fallback":
@@ -6459,7 +7511,7 @@ function buildMellowGroups(extraProxyGroup, nodelist, remarks) {
6459
7511
  continue;
6460
7512
  }
6461
7513
  const filtered = [];
6462
- for (const rule of group.proxies) {
7514
+ for (const rule of group2.proxies) {
6463
7515
  groupGenerate(rule, nodelist, filtered, false);
6464
7516
  }
6465
7517
  if (filtered.length === 0) {
@@ -6469,7 +7521,7 @@ function buildMellowGroups(extraProxyGroup, nodelist, remarks) {
6469
7521
  filtered.push(...remarks);
6470
7522
  }
6471
7523
  }
6472
- lines.push(`${group.name}, ${filtered.join(":")}, latency, interval=300, timeout=6`);
7524
+ lines.push(`${group2.name}, ${filtered.join(":")}, latency, interval=300, timeout=6`);
6473
7525
  }
6474
7526
  return lines;
6475
7527
  }
@@ -6519,7 +7571,7 @@ function toMellow(nodes, extraProxyGroup, settings, options) {
6519
7571
  function buildQuanNode(x, settings, remark, nodeList) {
6520
7572
  const hostname = x.hostname;
6521
7573
  const port = String(x.port);
6522
- const group = x.group;
7574
+ const group2 = x.group;
6523
7575
  let method = x.encryptMethod;
6524
7576
  const scv = x.allowInsecure !== void 0 ? x.allowInsecure : settings.skipCertVerify;
6525
7577
  let proxyStr;
@@ -6528,7 +7580,7 @@ function buildQuanNode(x, settings, remark, nodeList) {
6528
7580
  if (method === "auto") {
6529
7581
  method = "chacha20-ietf-poly1305";
6530
7582
  }
6531
- proxyStr = `${remark} = vmess, ${hostname}, ${port}, ${method}, "${x.userId}", group=${group}`;
7583
+ proxyStr = `${remark} = vmess, ${hostname}, ${port}, ${method}, "${x.userId}", group=${group2}`;
6532
7584
  if (x.tlsSecure) {
6533
7585
  proxyStr += `, over-tls=true, tls-host=${x.host}`;
6534
7586
  if (scv !== void 0) {
@@ -6549,10 +7601,10 @@ function buildQuanNode(x, settings, remark, nodeList) {
6549
7601
  case "ssr" /* ShadowsocksR */:
6550
7602
  if (nodeList) {
6551
7603
  proxyStr = `ssr://${urlSafeBase64Encode(
6552
- `${hostname}:${port}:${x.protocol}:${method}:${x.obfs}:${urlSafeBase64Encode(x.password)}/?group=${urlSafeBase64Encode(group)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(x.obfsParam)}&protoparam=${urlSafeBase64Encode(x.protocolParam)}`
7604
+ `${hostname}:${port}:${x.protocol}:${method}:${x.obfs}:${urlSafeBase64Encode(x.password)}/?group=${urlSafeBase64Encode(group2)}&remarks=${urlSafeBase64Encode(remark)}&obfsparam=${urlSafeBase64Encode(x.obfsParam)}&protoparam=${urlSafeBase64Encode(x.protocolParam)}`
6553
7605
  )}`;
6554
7606
  } else {
6555
- proxyStr = `${remark} = shadowsocksr, ${hostname}, ${port}, ${method}, "${x.password}", group=${group}, protocol=${x.protocol}, obfs=${x.obfs}`;
7607
+ proxyStr = `${remark} = shadowsocksr, ${hostname}, ${port}, ${method}, "${x.password}", group=${group2}, protocol=${x.protocol}, obfs=${x.obfs}`;
6556
7608
  if (x.protocolParam !== "") {
6557
7609
  proxyStr += `, protocol_param=${x.protocolParam}`;
6558
7610
  }
@@ -6567,9 +7619,9 @@ function buildQuanNode(x, settings, remark, nodeList) {
6567
7619
  if (x.plugin !== "" && x.pluginOption !== "") {
6568
7620
  proxyStr += `/?plugin=${urlEncode(`${x.plugin};${x.pluginOption}`)}`;
6569
7621
  }
6570
- proxyStr += `&group=${urlSafeBase64Encode(group)}#${urlEncode(remark)}`;
7622
+ proxyStr += `&group=${urlSafeBase64Encode(group2)}#${urlEncode(remark)}`;
6571
7623
  } else {
6572
- proxyStr = `${remark} = shadowsocks, ${hostname}, ${port}, ${method}, "${x.password}", group=${group}`;
7624
+ proxyStr = `${remark} = shadowsocks, ${hostname}, ${port}, ${method}, "${x.password}", group=${group2}`;
6573
7625
  if (x.plugin === "obfs-local" && x.pluginOption !== "") {
6574
7626
  proxyStr += `, ${x.pluginOption.replace(/;/g, ", ")}`;
6575
7627
  }
@@ -6577,7 +7629,7 @@ function buildQuanNode(x, settings, remark, nodeList) {
6577
7629
  break;
6578
7630
  case "http" /* HTTP */:
6579
7631
  case "https" /* HTTPS */:
6580
- proxyStr = `${remark} = http, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group}`;
7632
+ proxyStr = `${remark} = http, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group2}`;
6581
7633
  if (x.username !== "" && x.password !== "") {
6582
7634
  proxyStr += `, upstream-proxy-auth=true, upstream-proxy-username=${x.username}, upstream-proxy-password=${x.password}`;
6583
7635
  } else {
@@ -6597,7 +7649,7 @@ function buildQuanNode(x, settings, remark, nodeList) {
6597
7649
  }
6598
7650
  break;
6599
7651
  case "socks5" /* SOCKS5 */:
6600
- proxyStr = `${remark} = socks, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group}`;
7652
+ proxyStr = `${remark} = socks, upstream-proxy-address=${hostname}, upstream-proxy-port=${port}, group=${group2}`;
6601
7653
  if (x.username !== "" && x.password !== "") {
6602
7654
  proxyStr += `, upstream-proxy-auth=true, upstream-proxy-username=${x.username}, upstream-proxy-password=${x.password}`;
6603
7655
  } else {
@@ -6623,9 +7675,9 @@ function buildQuanNode(x, settings, remark, nodeList) {
6623
7675
  }
6624
7676
  function buildQuanGroups(extraProxyGroup, nodelist) {
6625
7677
  const lines = [];
6626
- for (const group of extraProxyGroup) {
7678
+ for (const group2 of extraProxyGroup) {
6627
7679
  let type;
6628
- switch (group.type) {
7680
+ switch (group2.type) {
6629
7681
  case "select":
6630
7682
  case "fallback":
6631
7683
  type = "static";
@@ -6637,10 +7689,10 @@ function buildQuanGroups(extraProxyGroup, nodelist) {
6637
7689
  type = "balance, round-robin";
6638
7690
  break;
6639
7691
  case "ssid": {
6640
- let singlegroup2 = `${group.name} : wifi = ${group.proxies[0] ?? ""}`;
7692
+ let singlegroup2 = `${group2.name} : wifi = ${group2.proxies[0] ?? ""}`;
6641
7693
  let content = "";
6642
7694
  let celluar = "";
6643
- for (const proxy of group.proxies.slice(1)) {
7695
+ for (const proxy of group2.proxies.slice(1)) {
6644
7696
  const m = /^(.*?),?celluar\s?=\s?(.*?)(,.*)$/.exec(proxy);
6645
7697
  if (!m) {
6646
7698
  content += `${proxy}
@@ -6663,7 +7715,7 @@ ${content.trim().replace(/^,|,$/g, "").replace(/,/g, "\n")}`;
6663
7715
  continue;
6664
7716
  }
6665
7717
  const filtered = [];
6666
- for (const rule of group.proxies) {
7718
+ for (const rule of group2.proxies) {
6667
7719
  groupGenerate(rule, nodelist, filtered, true);
6668
7720
  }
6669
7721
  if (filtered.length === 0) {
@@ -6673,7 +7725,7 @@ ${content.trim().replace(/^,|,$/g, "").replace(/,/g, "\n")}`;
6673
7725
  type = "static";
6674
7726
  }
6675
7727
  const proxies = filtered.join("\n");
6676
- let singlegroup = `${group.name} : ${type}`;
7728
+ let singlegroup = `${group2.name} : ${type}`;
6677
7729
  if (type === "static") {
6678
7730
  singlegroup += `, ${filtered[0]}`;
6679
7731
  }
@@ -6711,6 +7763,9 @@ function toQuan(nodes, extraProxyGroup, settings, options) {
6711
7763
  if (groupLines.length > 0) {
6712
7764
  setRawSection(sections, "POLICY", groupLines);
6713
7765
  }
7766
+ if (options.rules && options.rules.lines.length > 0) {
7767
+ setRawSection(sections, options.rules.section, options.rules.lines);
7768
+ }
6714
7769
  return renderRawIni(sections);
6715
7770
  }
6716
7771
 
@@ -6829,12 +7884,32 @@ function buildQuanxNode(x, settings, remark) {
6829
7884
  }
6830
7885
  }
6831
7886
  break;
6832
- case "anytls" /* AnyTLS */:
6833
- proxyStr = `anytls = ${hostname}:${port}, password=${x.password}`;
6834
- if (x.sni !== "") {
6835
- proxyStr += `, tls-host=${x.sni}`;
7887
+ case "vless" /* VLESS */: {
7888
+ const vmethod = method === "" ? "none" : method;
7889
+ proxyStr = `vless = ${hostname}:${port}, method=${vmethod}, password=${x.userId}`;
7890
+ if (tlsSecure && tls13 !== void 0) {
7891
+ proxyStr += `, tls13=${triStr2(tls13)}`;
7892
+ }
7893
+ const obfsHost = host !== "" ? host : x.serverName !== "" ? x.serverName : x.sni;
7894
+ if (x.transferProtocol === "ws") {
7895
+ proxyStr += tlsSecure ? ", obfs=wss" : ", obfs=ws";
7896
+ proxyStr += `, obfs-host=${obfsHost}, obfs-uri=${path}`;
7897
+ } else if (tlsSecure) {
7898
+ proxyStr += `, obfs=over-tls, obfs-host=${obfsHost}`;
7899
+ }
7900
+ if (x.publicKey !== "") {
7901
+ proxyStr += `, reality-base64-pubkey=${x.publicKey}`;
7902
+ }
7903
+ if (x.shortId !== "") {
7904
+ proxyStr += `, reality-hex-shortid=${x.shortId}`;
7905
+ }
7906
+ if (x.flow !== "") {
7907
+ proxyStr += `, vless-flow=${x.flow}`;
6836
7908
  }
6837
7909
  break;
7910
+ }
7911
+ // Quantumult X has no AnyTLS/Hysteria2/TUIC mapping — drop them instead
7912
+ // of emitting non-standard lines.
6838
7913
  default:
6839
7914
  return void 0;
6840
7915
  }
@@ -6852,10 +7927,10 @@ function buildQuanxNode(x, settings, remark) {
6852
7927
  }
6853
7928
  function buildQuanxGroups(extraProxyGroup, nodelist) {
6854
7929
  const lines = [];
6855
- for (const group of extraProxyGroup) {
7930
+ for (const group2 of extraProxyGroup) {
6856
7931
  let type;
6857
7932
  const filtered = [];
6858
- switch (group.type) {
7933
+ switch (group2.type) {
6859
7934
  case "select":
6860
7935
  type = "static";
6861
7936
  break;
@@ -6870,15 +7945,15 @@ function buildQuanxGroups(extraProxyGroup, nodelist) {
6870
7945
  break;
6871
7946
  case "ssid":
6872
7947
  type = "ssid";
6873
- for (const proxy of group.proxies) {
7948
+ for (const proxy of group2.proxies) {
6874
7949
  filtered.push(proxy.replace(/=/g, ":"));
6875
7950
  }
6876
7951
  break;
6877
7952
  default:
6878
7953
  continue;
6879
7954
  }
6880
- if (group.type !== "ssid") {
6881
- for (const rule of group.proxies) {
7955
+ if (group2.type !== "ssid") {
7956
+ for (const rule of group2.proxies) {
6882
7957
  groupGenerate(rule, nodelist, filtered, true);
6883
7958
  }
6884
7959
  if (filtered.length === 0) {
@@ -6889,11 +7964,11 @@ function buildQuanxGroups(extraProxyGroup, nodelist) {
6889
7964
  }
6890
7965
  }
6891
7966
  const proxies = filtered.join(", ");
6892
- let line = `${type}=${group.name}, ${proxies}`;
6893
- if (group.type !== "select" && group.type !== "ssid") {
6894
- line += `, check-interval=${group.interval}`;
6895
- if (group.tolerance > 0) {
6896
- line += `, tolerance=${group.tolerance}`;
7967
+ let line = `${type}=${group2.name}, ${proxies}`;
7968
+ if (group2.type !== "select" && group2.type !== "ssid") {
7969
+ line += `, check-interval=${group2.interval}`;
7970
+ if (group2.tolerance > 0) {
7971
+ line += `, tolerance=${group2.tolerance}`;
6897
7972
  }
6898
7973
  }
6899
7974
  lines.push(line);
@@ -6927,6 +8002,9 @@ function toQuanX(nodes, extraProxyGroup, settings, options) {
6927
8002
  if (groupLines.length > 0) {
6928
8003
  setRawSection(sections, "policy", groupLines);
6929
8004
  }
8005
+ if (options.rules && options.rules.lines.length > 0) {
8006
+ setRawSection(sections, options.rules.section, options.rules.lines);
8007
+ }
6930
8008
  return renderRawIni(sections);
6931
8009
  }
6932
8010
 
@@ -7022,18 +8100,9 @@ function buildOutbound(x, settings, remark) {
7022
8100
  proxy = out;
7023
8101
  break;
7024
8102
  }
8103
+ // sing-box has no SSR support — drop such nodes.
7025
8104
  case "ssr" /* ShadowsocksR */:
7026
- proxy = {
7027
- ...common,
7028
- method: x.encryptMethod,
7029
- obfs: x.obfs,
7030
- obfs_param: x.obfsParam,
7031
- password: x.password,
7032
- protocol: x.protocol,
7033
- protocol_param: x.protocolParam,
7034
- type: "shadowsocksr"
7035
- };
7036
- break;
8105
+ return void 0;
7037
8106
  case "vmess" /* VMess */: {
7038
8107
  const out = {
7039
8108
  ...common,
@@ -7049,6 +8118,18 @@ function buildOutbound(x, settings, remark) {
7049
8118
  proxy = out;
7050
8119
  break;
7051
8120
  }
8121
+ case "vless" /* VLESS */: {
8122
+ const out = { ...common, type: "vless", uuid: x.userId };
8123
+ if (x.flow !== "") {
8124
+ out.flow = x.flow;
8125
+ }
8126
+ const transport = buildTransport(x);
8127
+ if (transport) {
8128
+ out.transport = transport;
8129
+ }
8130
+ proxy = out;
8131
+ break;
8132
+ }
7052
8133
  case "trojan" /* Trojan */: {
7053
8134
  const out = { ...common, password: x.password, type: "trojan" };
7054
8135
  const transport = buildTransport(x);
@@ -7058,6 +8139,20 @@ function buildOutbound(x, settings, remark) {
7058
8139
  proxy = out;
7059
8140
  break;
7060
8141
  }
8142
+ case "tuic" /* TUIC */: {
8143
+ const out = {
8144
+ ...common,
8145
+ password: x.password,
8146
+ type: "tuic",
8147
+ udp_relay_mode: "native",
8148
+ uuid: x.userId
8149
+ };
8150
+ if (x.congestionControl !== "") {
8151
+ out.congestion_control = x.congestionControl;
8152
+ }
8153
+ proxy = out;
8154
+ break;
8155
+ }
7061
8156
  case "wireguard" /* WireGuard */: {
7062
8157
  const addresses = [x.selfIp];
7063
8158
  if (x.selfIpv6 !== "") {
@@ -7170,6 +8265,17 @@ function buildOutbound(x, settings, remark) {
7170
8265
  }
7171
8266
  const tls = buildTls(x, scv);
7172
8267
  if (tls) {
8268
+ if (x.type === "vless" /* VLESS */ && (x.publicKey !== "" || x.shortId !== "")) {
8269
+ const tlsRec = tls;
8270
+ if (x.fingerprint !== "") {
8271
+ tlsRec.utls = { enabled: true, fingerprint: x.fingerprint };
8272
+ }
8273
+ tlsRec.reality = {
8274
+ enabled: true,
8275
+ public_key: x.publicKey,
8276
+ short_id: x.shortId
8277
+ };
8278
+ }
7173
8279
  proxy.tls = tls;
7174
8280
  }
7175
8281
  if (udp !== void 0 && !udp) {
@@ -7214,9 +8320,9 @@ function toSingBox(nodes, extraProxyGroup, settings, options) {
7214
8320
  remarksList.push(remark);
7215
8321
  }
7216
8322
  if (!options.nodeList) {
7217
- for (const group of extraProxyGroup) {
8323
+ for (const group2 of extraProxyGroup) {
7218
8324
  let type;
7219
- switch (group.type) {
8325
+ switch (group2.type) {
7220
8326
  case "select":
7221
8327
  type = "selector";
7222
8328
  break;
@@ -7229,18 +8335,18 @@ function toSingBox(nodes, extraProxyGroup, settings, options) {
7229
8335
  continue;
7230
8336
  }
7231
8337
  const filtered = [];
7232
- for (const rule of group.proxies) {
8338
+ for (const rule of group2.proxies) {
7233
8339
  groupGenerate(rule, nodelist, filtered, true);
7234
8340
  }
7235
8341
  if (filtered.length === 0) {
7236
8342
  filtered.push("DIRECT");
7237
8343
  }
7238
- const entry = { outbounds: filtered, tag: group.name, type };
7239
- if (group.type === "url-test") {
7240
- entry.url = group.url;
7241
- entry.interval = formatSingBoxInterval(group.interval);
7242
- if (group.tolerance > 0) {
7243
- entry.tolerance = group.tolerance;
8344
+ const entry = { outbounds: filtered, tag: group2.name, type };
8345
+ if (group2.type === "url-test") {
8346
+ entry.url = group2.url;
8347
+ entry.interval = formatSingBoxInterval(group2.interval);
8348
+ if (group2.tolerance > 0) {
8349
+ entry.tolerance = group2.tolerance;
7244
8350
  }
7245
8351
  }
7246
8352
  outbounds.push(entry);
@@ -7260,12 +8366,30 @@ function toSingBox(nodes, extraProxyGroup, settings, options) {
7260
8366
  return JSON.stringify(doc, null, 2);
7261
8367
  }
7262
8368
 
8369
+ // src/generator/surfboard.ts
8370
+ function toSurfboard(nodes, extraProxyGroup, settings, options) {
8371
+ const filtered = nodes.filter(
8372
+ (n) => n.type !== "hysteria2" /* Hysteria2 */ && n.type !== "anytls" /* AnyTLS */
8373
+ );
8374
+ return toSurge(filtered, extraProxyGroup, settings, {
8375
+ baseConf: options.baseConf,
8376
+ nodeList: options.nodeList,
8377
+ rules: options.rules,
8378
+ version: 5
8379
+ });
8380
+ }
8381
+
7263
8382
  // src/engine.ts
7264
8383
  var V2RAY_TYPE_MASK = 1 | 2 | 4 | 8;
7265
8384
  function runPreprocess(nodes, settings) {
7266
8385
  preprocessNodes(nodes, {
7267
8386
  addEmoji: settings.addEmoji,
8387
+ autoCountry: settings.autoCountry,
8388
+ countryLocale: settings.countryLocale,
8389
+ countryResolver: settings.countryResolver,
7268
8390
  emojiArray: settings.emojiArray,
8391
+ nameRegexArray: settings.nameRegexArray,
8392
+ nameRemove: settings.nameRemove,
7269
8393
  removeEmoji: settings.removeEmoji,
7270
8394
  renameArray: settings.renameArray,
7271
8395
  sortFlag: settings.sortFlag
@@ -7277,56 +8401,62 @@ function runPreprocess(nodes, settings) {
7277
8401
  function generate(nodes, options) {
7278
8402
  const settings = { ...createExtraSettings(), ...options.settings };
7279
8403
  runPreprocess(nodes, settings);
8404
+ const groups = resolveGroups(options);
8405
+ const rulesets = resolveRulesets(options, settings);
7280
8406
  switch (options.target) {
7281
8407
  case "clash" /* Clash */:
7282
8408
  case "clashr" /* ClashR */: {
7283
- const { doc } = buildClash(
7284
- nodes,
7285
- options.groups ?? [],
7286
- settings,
7287
- options.target === "clashr" /* ClashR */
7288
- );
7289
- attachClashRules(doc, settings, options.rulesets);
8409
+ const { doc } = buildClash(nodes, groups, settings, options.target === "clashr" /* ClashR */);
8410
+ attachClashRules(doc, settings, rulesets);
7290
8411
  return stringifyClash(doc);
7291
8412
  }
7292
8413
  case "surge" /* Surge */: {
7293
- const surgeVersion = options.surgeVersion ?? 4;
7294
- const rules = options.rulesets ? rulesetToSurgeRules(options.rulesets, surgeVersion) : void 0;
7295
- return toSurge(nodes, options.groups ?? [], settings, {
8414
+ const surgeVersion = options.surgeVersion ?? 5;
8415
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, surgeVersion) : void 0;
8416
+ return toSurge(nodes, groups, settings, {
7296
8417
  baseConf: options.baseConf ?? "",
7297
8418
  nodeList: settings.nodeList,
7298
8419
  rules,
7299
8420
  version: surgeVersion
7300
8421
  });
7301
8422
  }
7302
- case "quanx" /* QuantumultX */:
7303
- return toQuanX(nodes, options.groups ?? [], settings, {
8423
+ case "quanx" /* QuantumultX */: {
8424
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, -1) : void 0;
8425
+ return toQuanX(nodes, groups, settings, {
7304
8426
  baseConf: options.baseConf ?? "",
7305
- nodeList: settings.nodeList
8427
+ nodeList: settings.nodeList,
8428
+ rules
7306
8429
  });
7307
- case "quan" /* Quantumult */:
7308
- return toQuan(nodes, options.groups ?? [], settings, {
8430
+ }
8431
+ case "quan" /* Quantumult */: {
8432
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, -2) : void 0;
8433
+ return toQuan(nodes, groups, settings, {
7309
8434
  baseConf: options.baseConf ?? "",
7310
- nodeList: settings.nodeList
8435
+ nodeList: settings.nodeList,
8436
+ rules
7311
8437
  });
7312
- case "loon" /* Loon */:
7313
- return toLoon(nodes, options.groups ?? [], settings, {
8438
+ }
8439
+ case "loon" /* Loon */: {
8440
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, -4) : void 0;
8441
+ return toLoon(nodes, groups, settings, {
7314
8442
  baseConf: options.baseConf ?? "",
7315
- nodeList: settings.nodeList
8443
+ nodeList: settings.nodeList,
8444
+ rules
7316
8445
  });
8446
+ }
7317
8447
  case "singbox" /* SingBox */:
7318
- return toSingBox(nodes, options.groups ?? [], settings, {
8448
+ return toSingBox(nodes, groups, settings, {
7319
8449
  baseConf: options.baseConf ?? "",
7320
8450
  nodeList: settings.nodeList,
7321
8451
  overwriteOriginalRules: settings.overwriteOriginalRules,
7322
- rulesets: options.rulesets
8452
+ rulesets
7323
8453
  });
7324
8454
  case "mellow" /* Mellow */:
7325
- return toMellow(nodes, options.groups ?? [], settings, {
8455
+ return toMellow(nodes, groups, settings, {
7326
8456
  baseConf: options.baseConf ?? "",
7327
8457
  nodeList: settings.nodeList,
7328
8458
  overwriteOriginalRules: settings.overwriteOriginalRules,
7329
- rulesets: options.rulesets
8459
+ rulesets
7330
8460
  });
7331
8461
  case "v2ray" /* V2Ray */:
7332
8462
  return proxyToSingle(nodes, 4, settings);
@@ -7340,6 +8470,16 @@ function generate(nodes, options) {
7340
8470
  return proxyToSingle(nodes, 2, settings);
7341
8471
  case "ssd" /* SSD */:
7342
8472
  return proxyToSsd(nodes, "", "");
8473
+ case "sssub" /* SSSub */:
8474
+ return proxyToSsSub(nodes, options.baseConf ?? "");
8475
+ case "surfboard" /* Surfboard */: {
8476
+ const rules = rulesets ? rulesetToSurgeRules(rulesets, options.surgeVersion ?? 5) : void 0;
8477
+ return toSurfboard(nodes, groups, settings, {
8478
+ baseConf: options.baseConf ?? "",
8479
+ nodeList: settings.nodeList,
8480
+ rules
8481
+ });
8482
+ }
7343
8483
  case "nodelist" /* NodeList */: {
7344
8484
  settings.nodeList = true;
7345
8485
  return proxyToSingle(nodes, V2RAY_TYPE_MASK, settings);
@@ -7351,7 +8491,28 @@ function generate(nodes, options) {
7351
8491
  function toSip008(nodes, baseConf = "") {
7352
8492
  return proxyToSsSub(nodes, baseConf);
7353
8493
  }
8494
+ function resolveGroups(options) {
8495
+ if (options.includeRules !== false && (!options.groups || options.groups.length === 0)) {
8496
+ return getDefaultProxyGroups();
8497
+ }
8498
+ return options.groups ?? [];
8499
+ }
8500
+ function resolveRulesets(options, settings) {
8501
+ if (options.includeRules === false) {
8502
+ return void 0;
8503
+ }
8504
+ if (!settings.enableRuleGenerator || settings.nodeList) {
8505
+ return void 0;
8506
+ }
8507
+ if (options.rulesets && options.rulesets.length > 0) {
8508
+ return options.rulesets;
8509
+ }
8510
+ return getDefaultRulesets();
8511
+ }
7354
8512
  function attachClashRules(doc, settings, rulesets) {
8513
+ if (!settings.enableRuleGenerator || settings.nodeList) {
8514
+ return;
8515
+ }
7355
8516
  if (!rulesets || rulesets.length === 0) {
7356
8517
  return;
7357
8518
  }