nodelistparser 0.3.1 → 1.0.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.
@@ -98,7 +98,7 @@ type SupportedConfig = HttpProxyConfig | SnellConfig | TrojanConfig | ShadowSock
98
98
  declare function decode$1(raw: string): SupportedConfig;
99
99
  declare function encode$1(config: SupportedConfig): string;
100
100
 
101
- declare namespace index$3 {
101
+ declare namespace index$4 {
102
102
  export { decode$1 as decode, encode$1 as encode };
103
103
  }
104
104
 
@@ -280,28 +280,34 @@ declare function encode(config: SupportedConfig): {
280
280
  network?: undefined;
281
281
  };
282
282
 
283
- declare const index$2_decode: typeof decode;
284
- declare const index$2_encode: typeof encode;
285
- declare namespace index$2 {
286
- export { index$2_decode as decode, index$2_encode as encode };
283
+ declare const index$3_decode: typeof decode;
284
+ declare const index$3_encode: typeof encode;
285
+ declare namespace index$3 {
286
+ export { index$3_decode as decode, index$3_encode as encode };
287
287
  }
288
288
 
289
289
  declare function decodeOne(sip002: string): ShadowSocksConfig;
290
290
  declare function decodeBase64Multiline(text: string): string[];
291
291
  declare function decodeMultiline(text: string): ShadowSocksConfig[];
292
292
 
293
- declare const index$1_decodeBase64Multiline: typeof decodeBase64Multiline;
294
- declare const index$1_decodeMultiline: typeof decodeMultiline;
295
- declare const index$1_decodeOne: typeof decodeOne;
293
+ declare const index$2_decodeBase64Multiline: typeof decodeBase64Multiline;
294
+ declare const index$2_decodeMultiline: typeof decodeMultiline;
295
+ declare const index$2_decodeOne: typeof decodeOne;
296
+ declare namespace index$2 {
297
+ export { index$2_decodeBase64Multiline as decodeBase64Multiline, index$2_decodeMultiline as decodeMultiline, index$2_decodeOne as decodeOne };
298
+ }
299
+
300
+ declare function parse$1(line: string): TrojanConfig;
301
+
296
302
  declare namespace index$1 {
297
- export { index$1_decodeBase64Multiline as decodeBase64Multiline, index$1_decodeMultiline as decodeMultiline, index$1_decodeOne as decodeOne };
303
+ export { parse$1 as parse };
298
304
  }
299
305
 
300
- declare function parse(line: string): TrojanConfig;
306
+ declare function parse(line: string): VmessConfig;
301
307
 
302
308
  declare const index_parse: typeof parse;
303
309
  declare namespace index {
304
310
  export { index_parse as parse };
305
311
  }
306
312
 
307
- export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type TuicV5Config, type VmessConfig, index$2 as clash, index$1 as ss, index$3 as surge, index as trojan };
313
+ export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type TuicV5Config, type VmessConfig, index$3 as clash, index$2 as ss, index$4 as surge, index$1 as trojan, index as vmess };
package/dist/cjs/index.js CHANGED
@@ -1,593 +1 @@
1
- var guard = require('foxts/guard');
2
-
3
- const boolean = (text)=>text === 'true';
4
- const number = Number;
5
- const comma = (text)=>text.split(',').map((piece)=>piece.trim());
6
- function assign(text) {
7
- const signIndex = text.indexOf('=');
8
- return signIndex === -1 ? [
9
- '',
10
- ''
11
- ] : [
12
- text.slice(0, signIndex).trim(),
13
- text.slice(signIndex + 1).trim()
14
- ];
15
- }
16
-
17
- const boolKeys = new Set([
18
- 'udp-relay',
19
- 'tfo',
20
- 'reuse',
21
- 'skip-cert-verify',
22
- 'tls',
23
- 'vmess-aead',
24
- 'ws'
25
- ]);
26
- const isProxyBoolKey = (key)=>boolKeys.has(key);
27
- const numKeys = new Set([
28
- 'version',
29
- 'download-bandwidth',
30
- 'port-hopping-interval',
31
- 'udp-port'
32
- ]);
33
- const isProxyNumKey = (key)=>numKeys.has(key);
34
- const arrKeys = new Set([]);
35
- const isProxyArrKey = (key)=>arrKeys.has(key);
36
- const strKeys = new Set([
37
- 'username',
38
- 'password',
39
- 'sni',
40
- 'encrypt-method',
41
- 'psk',
42
- 'obfs',
43
- 'obfs-host',
44
- 'uuid',
45
- 'alpn',
46
- 'block-quic',
47
- 'ws-path',
48
- 'ws-headers',
49
- 'port-hopping',
50
- 'token'
51
- ]);
52
- const isProxyStrKey = (key)=>strKeys.has(key);
53
- const UNSUPPORTED_VALUE = Symbol('unsupported');
54
- function decode$1(raw) {
55
- const parsePart = (part)=>{
56
- const [key, value] = assign(part);
57
- if (isProxyBoolKey(key)) {
58
- return [
59
- key,
60
- boolean(value)
61
- ];
62
- }
63
- if (isProxyNumKey(key)) {
64
- return [
65
- key,
66
- number(value)
67
- ];
68
- }
69
- if (isProxyArrKey(key)) {
70
- return [
71
- key,
72
- comma(value)
73
- ];
74
- }
75
- if (isProxyStrKey(key)) {
76
- if (value[0] === '"' && value.endsWith('"') || value[0] === '\'' && value.endsWith('\'')) {
77
- return [
78
- key,
79
- value.slice(1, -1)
80
- ];
81
- }
82
- return [
83
- key,
84
- value
85
- ];
86
- }
87
- return [
88
- key,
89
- UNSUPPORTED_VALUE
90
- ];
91
- };
92
- const [name, parts] = assign(raw);
93
- const [type, server, mayPort, ...rest] = comma(parts);
94
- const port = number(mayPort);
95
- const restDetails = Object.fromEntries(rest.map(parsePart));
96
- const shared = {
97
- raw,
98
- name,
99
- server,
100
- port,
101
- tfo: restDetails.tfo,
102
- blockQuic: restDetails['block-quic']
103
- };
104
- switch(type){
105
- case 'snell':
106
- {
107
- return {
108
- type: 'snell',
109
- psk: restDetails.psk,
110
- version: restDetails.version,
111
- reuse: restDetails.reuse,
112
- ...shared
113
- };
114
- }
115
- case 'ss':
116
- {
117
- return {
118
- type: 'ss',
119
- cipher: restDetails['encrypt-method'],
120
- password: restDetails.password,
121
- udp: restDetails['udp-relay'],
122
- obfs: restDetails.obfs,
123
- obfsHost: restDetails['obfs-host'],
124
- obfsUri: restDetails['obfs-uri'],
125
- udpPort: restDetails['udp-port'],
126
- ...shared
127
- };
128
- }
129
- case 'trojan':
130
- {
131
- return {
132
- type: 'trojan',
133
- password: restDetails.password,
134
- sni: restDetails.sni,
135
- skipCertVerify: restDetails['skip-cert-verify'],
136
- udp: restDetails['udp-relay'],
137
- ...shared
138
- };
139
- }
140
- case 'tuic':
141
- {
142
- return {
143
- type: 'tuic',
144
- sni: restDetails.sni,
145
- uuid: restDetails.uuid,
146
- alpn: restDetails.alpn,
147
- token: restDetails.token,
148
- skipCertVerify: restDetails['skip-cert-verify'],
149
- ...shared
150
- };
151
- }
152
- case 'tuic-v5':
153
- {
154
- return {
155
- type: 'tuic-v5',
156
- uuid: restDetails.uuid,
157
- alpn: restDetails.alpn,
158
- password: restDetails.password,
159
- sni: restDetails.sni,
160
- skipCertVerify: restDetails['skip-cert-verify'],
161
- ...shared
162
- };
163
- }
164
- case 'socks5':
165
- {
166
- return {
167
- type: 'socks5',
168
- username: rest[0],
169
- password: rest[1],
170
- udp: restDetails['udp-relay'],
171
- ...shared
172
- };
173
- }
174
- case 'http':
175
- {
176
- return {
177
- type: 'http',
178
- username: rest[0],
179
- password: rest[1],
180
- ...shared
181
- };
182
- }
183
- case 'vmess':
184
- {
185
- return {
186
- type: 'vmess',
187
- username: restDetails.username,
188
- tls: restDetails.tls,
189
- vmessAead: restDetails['vmess-aead'],
190
- ws: restDetails.ws,
191
- wsPath: restDetails['ws-path'],
192
- wsHeaders: restDetails['ws-headers'],
193
- skipCertVerify: restDetails['skip-cert-verify'],
194
- udp: restDetails['udp-relay'],
195
- sni: restDetails.sni,
196
- ...shared
197
- };
198
- }
199
- case 'hysteria2':
200
- return {
201
- type: 'hysteria2',
202
- password: restDetails.password,
203
- skipCertVerify: restDetails['skip-cert-verify'],
204
- downloadBandwidth: restDetails['download-bandwidth'],
205
- portHopping: restDetails['port-hopping'],
206
- portHoppingInterval: restDetails['port-hopping-interval'],
207
- ...shared
208
- };
209
- default:
210
- throw new TypeError(`Unsupported type: ${type} (surge decode)`);
211
- }
212
- // console.log({
213
- // name, type, server, port, restDetails
214
- // });
215
- }
216
- const joinString = (arr)=>arr.filter(Boolean).join(', ');
217
- function encode$1(config) {
218
- const shared = [
219
- config.tfo && 'tfo=true',
220
- config.blockQuic && `block-quic=${config.blockQuic}`
221
- ];
222
- switch(config.type){
223
- case 'snell':
224
- return joinString([
225
- `${config.name} = snell, ${config.server}, ${config.port}, psk=${config.psk}, version=${config.version}, reuse=${config.reuse}`,
226
- ...shared
227
- ]);
228
- case 'ss':
229
- return joinString([
230
- `${config.name} = ss, ${config.server}, ${config.port}, encrypt-method=${config.cipher}, password=${config.password}`,
231
- config.udp && 'udp-relay=true',
232
- config.udpPort && `udp-port=${config.udpPort}`,
233
- config.obfs && `obfs=${config.obfs}`,
234
- config.obfsHost && `obfs-host=${config.obfsHost}`,
235
- config.obfsUri && `obfs-uri=${config.obfsUri}`,
236
- ...shared
237
- ]);
238
- case 'trojan':
239
- return joinString([
240
- `${config.name} = trojan, ${config.server}, ${config.port}, password=${config.password}`,
241
- config.sni && `sni=${config.sni}`,
242
- config.skipCertVerify && 'skip-cert-verify=true',
243
- ...shared,
244
- config.udp && 'udp-relay=true'
245
- ]);
246
- case 'tuic':
247
- return joinString([
248
- `${config.name} = tuic, ${config.server}, ${config.port}, sni=${config.sni}, uuid=${config.uuid}, alpn=${config.alpn}, token=${config.token}`,
249
- ...shared
250
- ]);
251
- case 'socks5':
252
- return joinString([
253
- `${config.name} = socks5, ${config.server}, ${config.port}, ${config.username}, ${config.password}`,
254
- config.udp && 'udp-relay=true',
255
- ...shared
256
- ]);
257
- case 'http':
258
- return joinString([
259
- `${config.name} = http, ${config.server}, ${config.port}, ${config.username}, ${config.password}`,
260
- // no udp support for http
261
- ...shared
262
- ]);
263
- case 'vmess':
264
- return joinString([
265
- `${config.name} = vmess, ${config.server}, ${config.port}`,
266
- `username=${config.username}`,
267
- `tls=${config.tls}`,
268
- `vmess-aead=${config.vmessAead}`,
269
- 'ws=true',
270
- config.wsPath && `ws-path=${config.wsPath[0] === '/' ? config.wsPath : `/${config.wsPath}`}`,
271
- config.wsHeaders && `ws-headers=${config.wsHeaders}`,
272
- `skip-cert-verify=${config.skipCertVerify}`,
273
- `tfo=${config.tfo}`,
274
- `udp-relay=${config.udp}`
275
- ]);
276
- case 'hysteria2':
277
- return joinString([
278
- `${config.name} = hysteria2, ${config.server}, ${config.port}`,
279
- `password=${config.password}`,
280
- `download-bandwidth=${config.downloadBandwidth}`,
281
- config.portHopping && `port-hopping="${config.portHopping}"`,
282
- config.portHoppingInterval && `port-hopping-interval=${config.portHoppingInterval}`,
283
- `skip-cert-verify=${config.skipCertVerify}`,
284
- ...shared
285
- ]);
286
- case 'tuic-v5':
287
- return joinString([
288
- `${config.name} = tuic-v5, ${config.server}, ${config.port}`,
289
- `password=${config.password}`,
290
- `uuid=${config.uuid}`,
291
- `alpn=${config.alpn}`,
292
- `skip-cert-verify=${config.skipCertVerify}`,
293
- `sni=${config.sni}`,
294
- ...shared
295
- ]);
296
- default:
297
- guard.never(config, 'type (clash encode)');
298
- }
299
- }
300
-
301
- var index$3 = {
302
- __proto__: null,
303
- decode: decode$1,
304
- encode: encode$1
305
- };
306
-
307
- function decode(config) {
308
- if (!('type' in config) || typeof config.type !== 'string') {
309
- throw new TypeError('Missing or invalid type field');
310
- }
311
- const raw = JSON.stringify(config);
312
- switch(config.type){
313
- case 'http':
314
- return {
315
- type: 'http',
316
- name: config.name,
317
- server: config.server,
318
- port: Number(config.port),
319
- username: config.username,
320
- password: config.password,
321
- raw
322
- };
323
- case 'ss':
324
- return {
325
- type: 'ss',
326
- name: config.name,
327
- server: config.server,
328
- port: Number(config.port),
329
- cipher: config.cipher,
330
- password: config.password,
331
- udp: config.udp || false,
332
- obfs: config.plugin === 'obfs' ? config['plugin-opts'].mode : undefined,
333
- raw
334
- };
335
- case 'socks5':
336
- return {
337
- type: 'socks5',
338
- name: config.name,
339
- server: config.server,
340
- port: Number(config.port),
341
- username: config.username,
342
- password: config.password,
343
- udp: config.udp || false,
344
- raw
345
- };
346
- case 'trojan':
347
- return {
348
- type: 'trojan',
349
- name: config.name,
350
- server: config.server,
351
- port: Number(config.port),
352
- password: config.password,
353
- sni: config.sni,
354
- skipCertVerify: config['skip-cert-verify'] || false,
355
- udp: config.udp || false,
356
- raw
357
- };
358
- case 'vmess':
359
- return {
360
- type: 'vmess',
361
- name: config.name,
362
- server: config.server,
363
- port: Number(config.port),
364
- username: config.uuid,
365
- vmessAead: config.alterId === 1 || config.alterId === '1',
366
- sni: config.servername,
367
- ws: config.network === 'ws',
368
- wsPath: config['ws-path'],
369
- wsHeaders: config['ws-headers'] ? Object.entries(config['ws-headers']).map(([key, value])=>`${key}:${value}`).join(', ') : undefined,
370
- tls: config.tls || false,
371
- udp: config.udp ?? true,
372
- raw,
373
- skipCertVerify: config['skip-cert-verify'] || false
374
- };
375
- default:
376
- throw new TypeError(`Unsupported type: ${config.type} (clash decode)`);
377
- }
378
- }
379
- function encode(config) {
380
- const shared = {
381
- tfo: config.tfo
382
- };
383
- switch(config.type){
384
- case 'ss':
385
- return {
386
- name: config.name,
387
- type: 'ss',
388
- server: config.server,
389
- port: config.port,
390
- cipher: config.cipher,
391
- password: config.password,
392
- udp: config.udp,
393
- ...config.obfs ? {
394
- plugin: 'obfs',
395
- 'plugin-opts': {
396
- mode: config.obfs,
397
- host: config.obfsHost,
398
- uri: config.obfsUri
399
- }
400
- } : {},
401
- ...shared
402
- };
403
- case 'trojan':
404
- return {
405
- name: config.name,
406
- type: 'trojan',
407
- server: config.server,
408
- port: config.port,
409
- password: config.password,
410
- sni: config.sni,
411
- 'skip-cert-verify': config.skipCertVerify,
412
- udp: config.udp,
413
- ...shared
414
- };
415
- case 'tuic':
416
- case 'tuic-v5':
417
- return {
418
- name: config.name,
419
- type: 'tuic',
420
- server: config.server,
421
- port: config.port,
422
- sni: config.sni,
423
- uuid: config.uuid,
424
- alpn: config.alpn.split(',').map((x)=>x.trim()),
425
- ...config.type === 'tuic' ? {
426
- token: config.token
427
- } : {
428
- password: config.password
429
- },
430
- 'skip-cert-verify': config.skipCertVerify,
431
- udp: true,
432
- ...shared
433
- };
434
- case 'socks5':
435
- return {
436
- name: config.name,
437
- type: 'socks5',
438
- server: config.server,
439
- port: config.port,
440
- username: config.username,
441
- password: config.password,
442
- udp: config.udp,
443
- ...shared
444
- };
445
- case 'http':
446
- return {
447
- name: config.name,
448
- type: 'http',
449
- server: config.server,
450
- port: config.port,
451
- username: config.username,
452
- password: config.password,
453
- ...shared
454
- };
455
- case 'vmess':
456
- return {
457
- alterId: config.vmessAead ? 0 : undefined,
458
- tls: config.tls,
459
- udp: config.udp,
460
- uuid: config.username,
461
- name: config.name,
462
- servername: config.sni,
463
- 'ws-path': config.wsPath,
464
- server: config.server,
465
- 'ws-headers': config.wsHeaders ? parseStringToObject(config.wsHeaders) : undefined,
466
- cipher: 'auto',
467
- 'ws-opts': {
468
- path: config.wsPath,
469
- headers: config.wsHeaders ? parseStringToObject(config.wsHeaders) : undefined
470
- },
471
- type: 'vmess',
472
- port: config.port,
473
- network: config.ws ? 'ws' : 'tcp'
474
- };
475
- case 'hysteria2':
476
- return {
477
- name: config.name,
478
- type: 'hysteria2',
479
- server: config.server,
480
- port: config.port,
481
- ports: config.portHopping,
482
- password: config.password,
483
- down: config.downloadBandwidth + ' Mbps',
484
- 'skip-cert-verify': config.skipCertVerify
485
- };
486
- default:
487
- throw new TypeError(`Unsupported type: ${config.type} (clash encode)`);
488
- }
489
- }
490
- function parseStringToObject(input) {
491
- return input.split(',').reduce((acc, pair)=>{
492
- const [key, value] = pair.split(':');
493
- acc[key.trim()] = value.trim();
494
- return acc;
495
- }, {});
496
- }
497
-
498
- var index$2 = {
499
- __proto__: null,
500
- decode: decode,
501
- encode: encode
502
- };
503
-
504
- function decodeOne(sip002) {
505
- // ss://YWVzLTEyOC1nY206YzMxNWFhOGMtNGU1NC00MGRjLWJkYzctYzFjMjEwZjIxYTNi@ss1.meslink.xyz:10009#%F0%9F%87%AD%F0%9F%87%B0%20HK1%20HKT
506
- const [type, payload] = sip002.split('://');
507
- if (type !== 'ss') {
508
- throw new Error(`[ss.decodeOne] Unsupported type: ${type}`);
509
- }
510
- const [userInfo, server] = payload.split('@');
511
- let cipher, password;
512
- if (userInfo.includes(':')) {
513
- [cipher, password] = userInfo.split(':');
514
- } else {
515
- [cipher, password] = atob(userInfo).split(':');
516
- }
517
- const [serverName, _1] = server.split(':');
518
- const [_2, encodedName] = _1.split('#');
519
- const [port, pluginsStr] = _2.split('/');
520
- let plugin = null;
521
- if (pluginsStr) {
522
- try {
523
- plugin = new URLSearchParams(pluginsStr).get('plugin');
524
- } catch (e) {
525
- const err = new Error(`[ss.decodeOne] Invalid plugins: ${pluginsStr}`);
526
- err.cause = e;
527
- throw err;
528
- }
529
- }
530
- const pluginArgs = (plugin?.split(';') ?? []).reduce((acc, cur)=>{
531
- const [key, value] = cur.split('=');
532
- acc[key] = value;
533
- return acc;
534
- }, {});
535
- return {
536
- raw: sip002,
537
- type: 'ss',
538
- name: decodeURIComponent(encodedName),
539
- server: serverName,
540
- port: number(port),
541
- cipher,
542
- password,
543
- udp: true,
544
- obfs: 'obfs-local' in pluginArgs && 'obfs' in pluginArgs && (pluginArgs.obfs === 'http' || pluginArgs.obfs === 'tls') ? pluginArgs.obfs : undefined,
545
- obfsHost: 'obfs-host' in pluginArgs ? pluginArgs['obfs-host'] : undefined
546
- };
547
- }
548
- function decodeBase64Multiline(text) {
549
- return atob(text).replaceAll('\r\n', '\n').split('\n').filter(Boolean);
550
- }
551
- function decodeMultiline(text) {
552
- return decodeBase64Multiline(text).map((line)=>decodeOne(line));
553
- }
554
-
555
- var index$1 = {
556
- __proto__: null,
557
- decodeBase64Multiline: decodeBase64Multiline,
558
- decodeMultiline: decodeMultiline,
559
- decodeOne: decodeOne
560
- };
561
-
562
- function parse(line) {
563
- const url = new URL(line);
564
- // trojan://password@remote_host:remote_port
565
- const password = url.username;
566
- const server = url.hostname;
567
- const port = Number.parseInt(url.port, 10);
568
- if (Number.isNaN(port)) {
569
- throw new TypeError('invalid port: ' + url.port);
570
- }
571
- const name = decodeURIComponent(url.hash.slice(1));
572
- return {
573
- raw: line,
574
- name,
575
- type: 'trojan',
576
- server,
577
- port,
578
- password,
579
- udp: true,
580
- sni: url.searchParams.get('sni') ?? server,
581
- skipCertVerify: true
582
- };
583
- }
584
-
585
- var index = {
586
- __proto__: null,
587
- parse: parse
588
- };
589
-
590
- exports.clash = index$2;
591
- exports.ss = index$1;
592
- exports.surge = index$3;
593
- exports.trojan = index;
1
+ var e=require("foxts/guard"),s=require("node:buffer");const r=e=>"true"===e,t=Number,p=e=>e.split(",").map(e=>e.trim());function o(e){let s=e.indexOf("=");return -1===s?["",""]:[e.slice(0,s).trim(),e.slice(s+1).trim()]}const n=new Set(["udp-relay","tfo","reuse","skip-cert-verify","tls","vmess-aead","ws"]),a=e=>n.has(e),i=new Set(["version","download-bandwidth","port-hopping-interval","udp-port"]),u=e=>i.has(e),d=new Set([]),c=e=>d.has(e),l=new Set(["username","password","sni","encrypt-method","psk","obfs","obfs-host","uuid","alpn","block-quic","ws-path","ws-headers","port-hopping","token"]),w=e=>l.has(e),m=Symbol("unsupported"),f=e=>e.filter(Boolean).join(", ");function v(e){return e.split(",").reduce((e,s)=>{let[r,t]=s.split(":");return e[r.trim()]=t.trim(),e},{})}function y(e){let s,r;let[p,o]=e.split("://");if("ss"!==p)throw Error(`[ss.decodeOne] Unsupported type: ${p}`);let[n,a]=o.split("@");n.includes(":")?[s,r]=n.split(":"):[s,r]=atob(n).split(":");let[i,u]=a.split(":"),[d,c]=u.split("#"),[l,w]=d.split("/"),m=null;if(w)try{m=new URLSearchParams(w).get("plugin")}catch(s){let e=Error(`[ss.decodeOne] Invalid plugins: ${w}`);throw e.cause=s,e}let f=(m?.split(";")??[]).reduce((e,s)=>{let[r,t]=s.split("=");return e[r]=t,e},{});return{raw:e,type:"ss",name:decodeURIComponent(c),server:i,port:t(l),cipher:s,password:r,udp:!0,obfs:"obfs-local"in f&&"obfs"in f&&("http"===f.obfs||"tls"===f.obfs)?f.obfs:void 0,obfsHost:"obfs-host"in f?f["obfs-host"]:void 0}}function h(e){return atob(e).replaceAll("\r\n","\n").split("\n").filter(Boolean)}const $=new TextDecoder;exports.clash={__proto__:null,decode:function(e){if(!("type"in e)||"string"!=typeof e.type)throw TypeError("Missing or invalid type field");let s=JSON.stringify(e);switch(e.type){case"http":return{type:"http",name:e.name,server:e.server,port:Number(e.port),username:e.username,password:e.password,raw:s};case"ss":return{type:"ss",name:e.name,server:e.server,port:Number(e.port),cipher:e.cipher,password:e.password,udp:e.udp||!1,obfs:"obfs"===e.plugin?e["plugin-opts"].mode:void 0,raw:s};case"socks5":return{type:"socks5",name:e.name,server:e.server,port:Number(e.port),username:e.username,password:e.password,udp:e.udp||!1,raw:s};case"trojan":return{type:"trojan",name:e.name,server:e.server,port:Number(e.port),password:e.password,sni:e.sni,skipCertVerify:e["skip-cert-verify"]||!1,udp:e.udp||!1,raw:s};case"vmess":return{type:"vmess",name:e.name,server:e.server,port:Number(e.port),username:e.uuid,vmessAead:1===e.alterId||"1"===e.alterId,sni:e.servername,ws:"ws"===e.network,wsPath:e["ws-path"],wsHeaders:e["ws-headers"]?Object.entries(e["ws-headers"]).map(([e,s])=>`${e}:${s}`).join(", "):void 0,tls:e.tls||!1,udp:e.udp??!0,raw:s,skipCertVerify:e["skip-cert-verify"]||!1};default:throw TypeError(`Unsupported type: ${e.type} (clash decode)`)}},encode:function(e){let s={tfo:e.tfo};switch(e.type){case"ss":return{name:e.name,type:"ss",server:e.server,port:e.port,cipher:e.cipher,password:e.password,udp:e.udp,...e.obfs?{plugin:"obfs","plugin-opts":{mode:e.obfs,host:e.obfsHost,uri:e.obfsUri}}:{},...s};case"trojan":return{name:e.name,type:"trojan",server:e.server,port:e.port,password:e.password,sni:e.sni,"skip-cert-verify":e.skipCertVerify,udp:e.udp,...s};case"tuic":case"tuic-v5":return{name:e.name,type:"tuic",server:e.server,port:e.port,sni:e.sni,uuid:e.uuid,alpn:e.alpn.split(",").map(e=>e.trim()),..."tuic"===e.type?{token:e.token}:{password:e.password},"skip-cert-verify":e.skipCertVerify,udp:!0,...s};case"socks5":return{name:e.name,type:"socks5",server:e.server,port:e.port,username:e.username,password:e.password,udp:e.udp,...s};case"http":return{name:e.name,type:"http",server:e.server,port:e.port,username:e.username,password:e.password,...s};case"vmess":return{alterId:e.vmessAead?0:void 0,tls:e.tls,udp:e.udp,uuid:e.username,name:e.name,servername:e.sni,"ws-path":e.wsPath,server:e.server,"ws-headers":e.wsHeaders?v(e.wsHeaders):void 0,cipher:"auto","ws-opts":{path:e.wsPath,headers:e.wsHeaders?v(e.wsHeaders):void 0},type:"vmess",port:e.port,network:e.ws?"ws":"tcp"};case"hysteria2":return{name:e.name,type:"hysteria2",server:e.server,port:e.port,ports:e.portHopping,password:e.password,down:e.downloadBandwidth+" Mbps","skip-cert-verify":e.skipCertVerify};default:throw TypeError(`Unsupported type: ${e.type} (clash encode)`)}}},exports.ss={__proto__:null,decodeBase64Multiline:h,decodeMultiline:function(e){return h(e).map(e=>y(e))},decodeOne:y},exports.surge={__proto__:null,decode:function(e){let[s,n]=o(e),[i,d,l,...f]=p(n),v=t(l),y=Object.fromEntries(f.map(e=>{let[s,n]=o(e);return a(s)?[s,r(n)]:u(s)?[s,t(n)]:c(s)?[s,p(n)]:w(s)?'"'===n[0]&&n.endsWith('"')||"'"===n[0]&&n.endsWith("'")?[s,n.slice(1,-1)]:[s,n]:[s,m]})),h={raw:e,name:s,server:d,port:v,tfo:y.tfo,blockQuic:y["block-quic"]};switch(i){case"snell":return{type:"snell",psk:y.psk,version:y.version,reuse:y.reuse,...h};case"ss":return{type:"ss",cipher:y["encrypt-method"],password:y.password,udp:y["udp-relay"],obfs:y.obfs,obfsHost:y["obfs-host"],obfsUri:y["obfs-uri"],udpPort:y["udp-port"],...h};case"trojan":return{type:"trojan",password:y.password,sni:y.sni,skipCertVerify:y["skip-cert-verify"],udp:y["udp-relay"],...h};case"tuic":return{type:"tuic",sni:y.sni,uuid:y.uuid,alpn:y.alpn,token:y.token,skipCertVerify:y["skip-cert-verify"],...h};case"tuic-v5":return{type:"tuic-v5",uuid:y.uuid,alpn:y.alpn,password:y.password,sni:y.sni,skipCertVerify:y["skip-cert-verify"],...h};case"socks5":return{type:"socks5",username:f[0],password:f[1],udp:y["udp-relay"],...h};case"http":return{type:"http",username:f[0],password:f[1],...h};case"vmess":return{type:"vmess",username:y.username,tls:y.tls,vmessAead:y["vmess-aead"],ws:y.ws,wsPath:y["ws-path"],wsHeaders:y["ws-headers"],skipCertVerify:y["skip-cert-verify"],udp:y["udp-relay"],sni:y.sni,...h};case"hysteria2":return{type:"hysteria2",password:y.password,skipCertVerify:y["skip-cert-verify"],downloadBandwidth:y["download-bandwidth"],portHopping:y["port-hopping"],portHoppingInterval:y["port-hopping-interval"],...h};default:throw TypeError(`Unsupported type: ${i} (surge decode)`)}},encode:function(s){let r=[s.tfo&&"tfo=true",s.blockQuic&&`block-quic=${s.blockQuic}`];switch(s.type){case"snell":return f([`${s.name} = snell, ${s.server}, ${s.port}, psk=${s.psk}, version=${s.version}, reuse=${s.reuse}`,...r]);case"ss":return f([`${s.name} = ss, ${s.server}, ${s.port}, encrypt-method=${s.cipher}, password=${s.password}`,s.udp&&"udp-relay=true",s.udpPort&&`udp-port=${s.udpPort}`,s.obfs&&`obfs=${s.obfs}`,s.obfsHost&&`obfs-host=${s.obfsHost}`,s.obfsUri&&`obfs-uri=${s.obfsUri}`,...r]);case"trojan":return f([`${s.name} = trojan, ${s.server}, ${s.port}, password=${s.password}`,s.sni&&`sni=${s.sni}`,s.skipCertVerify&&"skip-cert-verify=true",...r,s.udp&&"udp-relay=true"]);case"tuic":return f([`${s.name} = tuic, ${s.server}, ${s.port}, sni=${s.sni}, uuid=${s.uuid}, alpn=${s.alpn}, token=${s.token}`,...r]);case"socks5":return f([`${s.name} = socks5, ${s.server}, ${s.port}, ${s.username}, ${s.password}`,s.udp&&"udp-relay=true",...r]);case"http":return f([`${s.name} = http, ${s.server}, ${s.port}, ${s.username}, ${s.password}`,...r]);case"vmess":return f([`${s.name} = vmess, ${s.server}, ${s.port}`,`username=${s.username}`,`tls=${s.tls}`,`vmess-aead=${s.vmessAead}`,"ws=true",s.wsPath&&`ws-path=${"/"===s.wsPath[0]?s.wsPath:`/${s.wsPath}`}`,s.wsHeaders&&`ws-headers=${s.wsHeaders}`,`skip-cert-verify=${s.skipCertVerify}`,`tfo=${s.tfo}`,`udp-relay=${s.udp}`]);case"hysteria2":return f([`${s.name} = hysteria2, ${s.server}, ${s.port}`,`password=${s.password}`,`download-bandwidth=${s.downloadBandwidth}`,s.portHopping&&`port-hopping="${s.portHopping}"`,s.portHoppingInterval&&`port-hopping-interval=${s.portHoppingInterval}`,`skip-cert-verify=${s.skipCertVerify}`,...r]);case"tuic-v5":return f([`${s.name} = tuic-v5, ${s.server}, ${s.port}`,`password=${s.password}`,`uuid=${s.uuid}`,`alpn=${s.alpn}`,`skip-cert-verify=${s.skipCertVerify}`,`sni=${s.sni}`,...r]);default:e.never(s,"type (clash encode)")}}},exports.trojan={__proto__:null,parse:function(e){let s=new URL(e),r=s.username,t=s.hostname,p=Number.parseInt(s.port,10);if(Number.isNaN(p))throw TypeError("invalid port: "+s.port);return{raw:e,name:decodeURIComponent(s.hash.slice(1)),type:"trojan",server:t,port:p,password:r,udp:!0,sni:s.searchParams.get("sni")??t,skipCertVerify:!0}}},exports.vmess={__proto__:null,parse:function(e){let r=JSON.parse($.decode(s.Buffer.from(e.slice(8),"base64"))),t=r.ps,p=r.path;return{raw:e,name:t,server:r.add,port:Number.parseInt(r.port,10),type:"vmess",username:r.id,tls:r.tls,vmessAead:"0"===r.aid,sni:r.sni,ws:"ws"===r.net,wsPath:"/"===p[0]?p:`/${p}`,wsHeaders:r.host?`Host:${r.host}`:void 0,skipCertVerify:!0,udp:!0}}};
@@ -98,7 +98,7 @@ type SupportedConfig = HttpProxyConfig | SnellConfig | TrojanConfig | ShadowSock
98
98
  declare function decode$1(raw: string): SupportedConfig;
99
99
  declare function encode$1(config: SupportedConfig): string;
100
100
 
101
- declare namespace index$3 {
101
+ declare namespace index$4 {
102
102
  export { decode$1 as decode, encode$1 as encode };
103
103
  }
104
104
 
@@ -280,28 +280,34 @@ declare function encode(config: SupportedConfig): {
280
280
  network?: undefined;
281
281
  };
282
282
 
283
- declare const index$2_decode: typeof decode;
284
- declare const index$2_encode: typeof encode;
285
- declare namespace index$2 {
286
- export { index$2_decode as decode, index$2_encode as encode };
283
+ declare const index$3_decode: typeof decode;
284
+ declare const index$3_encode: typeof encode;
285
+ declare namespace index$3 {
286
+ export { index$3_decode as decode, index$3_encode as encode };
287
287
  }
288
288
 
289
289
  declare function decodeOne(sip002: string): ShadowSocksConfig;
290
290
  declare function decodeBase64Multiline(text: string): string[];
291
291
  declare function decodeMultiline(text: string): ShadowSocksConfig[];
292
292
 
293
- declare const index$1_decodeBase64Multiline: typeof decodeBase64Multiline;
294
- declare const index$1_decodeMultiline: typeof decodeMultiline;
295
- declare const index$1_decodeOne: typeof decodeOne;
293
+ declare const index$2_decodeBase64Multiline: typeof decodeBase64Multiline;
294
+ declare const index$2_decodeMultiline: typeof decodeMultiline;
295
+ declare const index$2_decodeOne: typeof decodeOne;
296
+ declare namespace index$2 {
297
+ export { index$2_decodeBase64Multiline as decodeBase64Multiline, index$2_decodeMultiline as decodeMultiline, index$2_decodeOne as decodeOne };
298
+ }
299
+
300
+ declare function parse$1(line: string): TrojanConfig;
301
+
296
302
  declare namespace index$1 {
297
- export { index$1_decodeBase64Multiline as decodeBase64Multiline, index$1_decodeMultiline as decodeMultiline, index$1_decodeOne as decodeOne };
303
+ export { parse$1 as parse };
298
304
  }
299
305
 
300
- declare function parse(line: string): TrojanConfig;
306
+ declare function parse(line: string): VmessConfig;
301
307
 
302
308
  declare const index_parse: typeof parse;
303
309
  declare namespace index {
304
310
  export { index_parse as parse };
305
311
  }
306
312
 
307
- export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type TuicV5Config, type VmessConfig, index$2 as clash, index$1 as ss, index$3 as surge, index as trojan };
313
+ export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type TuicV5Config, type VmessConfig, index$3 as clash, index$2 as ss, index$4 as surge, index$1 as trojan, index as vmess };
package/dist/es/index.mjs CHANGED
@@ -1,590 +1 @@
1
- import { never } from 'foxts/guard';
2
-
3
- const boolean = (text)=>text === 'true';
4
- const number = Number;
5
- const comma = (text)=>text.split(',').map((piece)=>piece.trim());
6
- function assign(text) {
7
- const signIndex = text.indexOf('=');
8
- return signIndex === -1 ? [
9
- '',
10
- ''
11
- ] : [
12
- text.slice(0, signIndex).trim(),
13
- text.slice(signIndex + 1).trim()
14
- ];
15
- }
16
-
17
- const boolKeys = new Set([
18
- 'udp-relay',
19
- 'tfo',
20
- 'reuse',
21
- 'skip-cert-verify',
22
- 'tls',
23
- 'vmess-aead',
24
- 'ws'
25
- ]);
26
- const isProxyBoolKey = (key)=>boolKeys.has(key);
27
- const numKeys = new Set([
28
- 'version',
29
- 'download-bandwidth',
30
- 'port-hopping-interval',
31
- 'udp-port'
32
- ]);
33
- const isProxyNumKey = (key)=>numKeys.has(key);
34
- const arrKeys = new Set([]);
35
- const isProxyArrKey = (key)=>arrKeys.has(key);
36
- const strKeys = new Set([
37
- 'username',
38
- 'password',
39
- 'sni',
40
- 'encrypt-method',
41
- 'psk',
42
- 'obfs',
43
- 'obfs-host',
44
- 'uuid',
45
- 'alpn',
46
- 'block-quic',
47
- 'ws-path',
48
- 'ws-headers',
49
- 'port-hopping',
50
- 'token'
51
- ]);
52
- const isProxyStrKey = (key)=>strKeys.has(key);
53
- const UNSUPPORTED_VALUE = Symbol('unsupported');
54
- function decode$1(raw) {
55
- const parsePart = (part)=>{
56
- const [key, value] = assign(part);
57
- if (isProxyBoolKey(key)) {
58
- return [
59
- key,
60
- boolean(value)
61
- ];
62
- }
63
- if (isProxyNumKey(key)) {
64
- return [
65
- key,
66
- number(value)
67
- ];
68
- }
69
- if (isProxyArrKey(key)) {
70
- return [
71
- key,
72
- comma(value)
73
- ];
74
- }
75
- if (isProxyStrKey(key)) {
76
- if (value[0] === '"' && value.endsWith('"') || value[0] === '\'' && value.endsWith('\'')) {
77
- return [
78
- key,
79
- value.slice(1, -1)
80
- ];
81
- }
82
- return [
83
- key,
84
- value
85
- ];
86
- }
87
- return [
88
- key,
89
- UNSUPPORTED_VALUE
90
- ];
91
- };
92
- const [name, parts] = assign(raw);
93
- const [type, server, mayPort, ...rest] = comma(parts);
94
- const port = number(mayPort);
95
- const restDetails = Object.fromEntries(rest.map(parsePart));
96
- const shared = {
97
- raw,
98
- name,
99
- server,
100
- port,
101
- tfo: restDetails.tfo,
102
- blockQuic: restDetails['block-quic']
103
- };
104
- switch(type){
105
- case 'snell':
106
- {
107
- return {
108
- type: 'snell',
109
- psk: restDetails.psk,
110
- version: restDetails.version,
111
- reuse: restDetails.reuse,
112
- ...shared
113
- };
114
- }
115
- case 'ss':
116
- {
117
- return {
118
- type: 'ss',
119
- cipher: restDetails['encrypt-method'],
120
- password: restDetails.password,
121
- udp: restDetails['udp-relay'],
122
- obfs: restDetails.obfs,
123
- obfsHost: restDetails['obfs-host'],
124
- obfsUri: restDetails['obfs-uri'],
125
- udpPort: restDetails['udp-port'],
126
- ...shared
127
- };
128
- }
129
- case 'trojan':
130
- {
131
- return {
132
- type: 'trojan',
133
- password: restDetails.password,
134
- sni: restDetails.sni,
135
- skipCertVerify: restDetails['skip-cert-verify'],
136
- udp: restDetails['udp-relay'],
137
- ...shared
138
- };
139
- }
140
- case 'tuic':
141
- {
142
- return {
143
- type: 'tuic',
144
- sni: restDetails.sni,
145
- uuid: restDetails.uuid,
146
- alpn: restDetails.alpn,
147
- token: restDetails.token,
148
- skipCertVerify: restDetails['skip-cert-verify'],
149
- ...shared
150
- };
151
- }
152
- case 'tuic-v5':
153
- {
154
- return {
155
- type: 'tuic-v5',
156
- uuid: restDetails.uuid,
157
- alpn: restDetails.alpn,
158
- password: restDetails.password,
159
- sni: restDetails.sni,
160
- skipCertVerify: restDetails['skip-cert-verify'],
161
- ...shared
162
- };
163
- }
164
- case 'socks5':
165
- {
166
- return {
167
- type: 'socks5',
168
- username: rest[0],
169
- password: rest[1],
170
- udp: restDetails['udp-relay'],
171
- ...shared
172
- };
173
- }
174
- case 'http':
175
- {
176
- return {
177
- type: 'http',
178
- username: rest[0],
179
- password: rest[1],
180
- ...shared
181
- };
182
- }
183
- case 'vmess':
184
- {
185
- return {
186
- type: 'vmess',
187
- username: restDetails.username,
188
- tls: restDetails.tls,
189
- vmessAead: restDetails['vmess-aead'],
190
- ws: restDetails.ws,
191
- wsPath: restDetails['ws-path'],
192
- wsHeaders: restDetails['ws-headers'],
193
- skipCertVerify: restDetails['skip-cert-verify'],
194
- udp: restDetails['udp-relay'],
195
- sni: restDetails.sni,
196
- ...shared
197
- };
198
- }
199
- case 'hysteria2':
200
- return {
201
- type: 'hysteria2',
202
- password: restDetails.password,
203
- skipCertVerify: restDetails['skip-cert-verify'],
204
- downloadBandwidth: restDetails['download-bandwidth'],
205
- portHopping: restDetails['port-hopping'],
206
- portHoppingInterval: restDetails['port-hopping-interval'],
207
- ...shared
208
- };
209
- default:
210
- throw new TypeError(`Unsupported type: ${type} (surge decode)`);
211
- }
212
- // console.log({
213
- // name, type, server, port, restDetails
214
- // });
215
- }
216
- const joinString = (arr)=>arr.filter(Boolean).join(', ');
217
- function encode$1(config) {
218
- const shared = [
219
- config.tfo && 'tfo=true',
220
- config.blockQuic && `block-quic=${config.blockQuic}`
221
- ];
222
- switch(config.type){
223
- case 'snell':
224
- return joinString([
225
- `${config.name} = snell, ${config.server}, ${config.port}, psk=${config.psk}, version=${config.version}, reuse=${config.reuse}`,
226
- ...shared
227
- ]);
228
- case 'ss':
229
- return joinString([
230
- `${config.name} = ss, ${config.server}, ${config.port}, encrypt-method=${config.cipher}, password=${config.password}`,
231
- config.udp && 'udp-relay=true',
232
- config.udpPort && `udp-port=${config.udpPort}`,
233
- config.obfs && `obfs=${config.obfs}`,
234
- config.obfsHost && `obfs-host=${config.obfsHost}`,
235
- config.obfsUri && `obfs-uri=${config.obfsUri}`,
236
- ...shared
237
- ]);
238
- case 'trojan':
239
- return joinString([
240
- `${config.name} = trojan, ${config.server}, ${config.port}, password=${config.password}`,
241
- config.sni && `sni=${config.sni}`,
242
- config.skipCertVerify && 'skip-cert-verify=true',
243
- ...shared,
244
- config.udp && 'udp-relay=true'
245
- ]);
246
- case 'tuic':
247
- return joinString([
248
- `${config.name} = tuic, ${config.server}, ${config.port}, sni=${config.sni}, uuid=${config.uuid}, alpn=${config.alpn}, token=${config.token}`,
249
- ...shared
250
- ]);
251
- case 'socks5':
252
- return joinString([
253
- `${config.name} = socks5, ${config.server}, ${config.port}, ${config.username}, ${config.password}`,
254
- config.udp && 'udp-relay=true',
255
- ...shared
256
- ]);
257
- case 'http':
258
- return joinString([
259
- `${config.name} = http, ${config.server}, ${config.port}, ${config.username}, ${config.password}`,
260
- // no udp support for http
261
- ...shared
262
- ]);
263
- case 'vmess':
264
- return joinString([
265
- `${config.name} = vmess, ${config.server}, ${config.port}`,
266
- `username=${config.username}`,
267
- `tls=${config.tls}`,
268
- `vmess-aead=${config.vmessAead}`,
269
- 'ws=true',
270
- config.wsPath && `ws-path=${config.wsPath[0] === '/' ? config.wsPath : `/${config.wsPath}`}`,
271
- config.wsHeaders && `ws-headers=${config.wsHeaders}`,
272
- `skip-cert-verify=${config.skipCertVerify}`,
273
- `tfo=${config.tfo}`,
274
- `udp-relay=${config.udp}`
275
- ]);
276
- case 'hysteria2':
277
- return joinString([
278
- `${config.name} = hysteria2, ${config.server}, ${config.port}`,
279
- `password=${config.password}`,
280
- `download-bandwidth=${config.downloadBandwidth}`,
281
- config.portHopping && `port-hopping="${config.portHopping}"`,
282
- config.portHoppingInterval && `port-hopping-interval=${config.portHoppingInterval}`,
283
- `skip-cert-verify=${config.skipCertVerify}`,
284
- ...shared
285
- ]);
286
- case 'tuic-v5':
287
- return joinString([
288
- `${config.name} = tuic-v5, ${config.server}, ${config.port}`,
289
- `password=${config.password}`,
290
- `uuid=${config.uuid}`,
291
- `alpn=${config.alpn}`,
292
- `skip-cert-verify=${config.skipCertVerify}`,
293
- `sni=${config.sni}`,
294
- ...shared
295
- ]);
296
- default:
297
- never(config, 'type (clash encode)');
298
- }
299
- }
300
-
301
- var index$3 = {
302
- __proto__: null,
303
- decode: decode$1,
304
- encode: encode$1
305
- };
306
-
307
- function decode(config) {
308
- if (!('type' in config) || typeof config.type !== 'string') {
309
- throw new TypeError('Missing or invalid type field');
310
- }
311
- const raw = JSON.stringify(config);
312
- switch(config.type){
313
- case 'http':
314
- return {
315
- type: 'http',
316
- name: config.name,
317
- server: config.server,
318
- port: Number(config.port),
319
- username: config.username,
320
- password: config.password,
321
- raw
322
- };
323
- case 'ss':
324
- return {
325
- type: 'ss',
326
- name: config.name,
327
- server: config.server,
328
- port: Number(config.port),
329
- cipher: config.cipher,
330
- password: config.password,
331
- udp: config.udp || false,
332
- obfs: config.plugin === 'obfs' ? config['plugin-opts'].mode : undefined,
333
- raw
334
- };
335
- case 'socks5':
336
- return {
337
- type: 'socks5',
338
- name: config.name,
339
- server: config.server,
340
- port: Number(config.port),
341
- username: config.username,
342
- password: config.password,
343
- udp: config.udp || false,
344
- raw
345
- };
346
- case 'trojan':
347
- return {
348
- type: 'trojan',
349
- name: config.name,
350
- server: config.server,
351
- port: Number(config.port),
352
- password: config.password,
353
- sni: config.sni,
354
- skipCertVerify: config['skip-cert-verify'] || false,
355
- udp: config.udp || false,
356
- raw
357
- };
358
- case 'vmess':
359
- return {
360
- type: 'vmess',
361
- name: config.name,
362
- server: config.server,
363
- port: Number(config.port),
364
- username: config.uuid,
365
- vmessAead: config.alterId === 1 || config.alterId === '1',
366
- sni: config.servername,
367
- ws: config.network === 'ws',
368
- wsPath: config['ws-path'],
369
- wsHeaders: config['ws-headers'] ? Object.entries(config['ws-headers']).map(([key, value])=>`${key}:${value}`).join(', ') : undefined,
370
- tls: config.tls || false,
371
- udp: config.udp ?? true,
372
- raw,
373
- skipCertVerify: config['skip-cert-verify'] || false
374
- };
375
- default:
376
- throw new TypeError(`Unsupported type: ${config.type} (clash decode)`);
377
- }
378
- }
379
- function encode(config) {
380
- const shared = {
381
- tfo: config.tfo
382
- };
383
- switch(config.type){
384
- case 'ss':
385
- return {
386
- name: config.name,
387
- type: 'ss',
388
- server: config.server,
389
- port: config.port,
390
- cipher: config.cipher,
391
- password: config.password,
392
- udp: config.udp,
393
- ...config.obfs ? {
394
- plugin: 'obfs',
395
- 'plugin-opts': {
396
- mode: config.obfs,
397
- host: config.obfsHost,
398
- uri: config.obfsUri
399
- }
400
- } : {},
401
- ...shared
402
- };
403
- case 'trojan':
404
- return {
405
- name: config.name,
406
- type: 'trojan',
407
- server: config.server,
408
- port: config.port,
409
- password: config.password,
410
- sni: config.sni,
411
- 'skip-cert-verify': config.skipCertVerify,
412
- udp: config.udp,
413
- ...shared
414
- };
415
- case 'tuic':
416
- case 'tuic-v5':
417
- return {
418
- name: config.name,
419
- type: 'tuic',
420
- server: config.server,
421
- port: config.port,
422
- sni: config.sni,
423
- uuid: config.uuid,
424
- alpn: config.alpn.split(',').map((x)=>x.trim()),
425
- ...config.type === 'tuic' ? {
426
- token: config.token
427
- } : {
428
- password: config.password
429
- },
430
- 'skip-cert-verify': config.skipCertVerify,
431
- udp: true,
432
- ...shared
433
- };
434
- case 'socks5':
435
- return {
436
- name: config.name,
437
- type: 'socks5',
438
- server: config.server,
439
- port: config.port,
440
- username: config.username,
441
- password: config.password,
442
- udp: config.udp,
443
- ...shared
444
- };
445
- case 'http':
446
- return {
447
- name: config.name,
448
- type: 'http',
449
- server: config.server,
450
- port: config.port,
451
- username: config.username,
452
- password: config.password,
453
- ...shared
454
- };
455
- case 'vmess':
456
- return {
457
- alterId: config.vmessAead ? 0 : undefined,
458
- tls: config.tls,
459
- udp: config.udp,
460
- uuid: config.username,
461
- name: config.name,
462
- servername: config.sni,
463
- 'ws-path': config.wsPath,
464
- server: config.server,
465
- 'ws-headers': config.wsHeaders ? parseStringToObject(config.wsHeaders) : undefined,
466
- cipher: 'auto',
467
- 'ws-opts': {
468
- path: config.wsPath,
469
- headers: config.wsHeaders ? parseStringToObject(config.wsHeaders) : undefined
470
- },
471
- type: 'vmess',
472
- port: config.port,
473
- network: config.ws ? 'ws' : 'tcp'
474
- };
475
- case 'hysteria2':
476
- return {
477
- name: config.name,
478
- type: 'hysteria2',
479
- server: config.server,
480
- port: config.port,
481
- ports: config.portHopping,
482
- password: config.password,
483
- down: config.downloadBandwidth + ' Mbps',
484
- 'skip-cert-verify': config.skipCertVerify
485
- };
486
- default:
487
- throw new TypeError(`Unsupported type: ${config.type} (clash encode)`);
488
- }
489
- }
490
- function parseStringToObject(input) {
491
- return input.split(',').reduce((acc, pair)=>{
492
- const [key, value] = pair.split(':');
493
- acc[key.trim()] = value.trim();
494
- return acc;
495
- }, {});
496
- }
497
-
498
- var index$2 = {
499
- __proto__: null,
500
- decode: decode,
501
- encode: encode
502
- };
503
-
504
- function decodeOne(sip002) {
505
- // ss://YWVzLTEyOC1nY206YzMxNWFhOGMtNGU1NC00MGRjLWJkYzctYzFjMjEwZjIxYTNi@ss1.meslink.xyz:10009#%F0%9F%87%AD%F0%9F%87%B0%20HK1%20HKT
506
- const [type, payload] = sip002.split('://');
507
- if (type !== 'ss') {
508
- throw new Error(`[ss.decodeOne] Unsupported type: ${type}`);
509
- }
510
- const [userInfo, server] = payload.split('@');
511
- let cipher, password;
512
- if (userInfo.includes(':')) {
513
- [cipher, password] = userInfo.split(':');
514
- } else {
515
- [cipher, password] = atob(userInfo).split(':');
516
- }
517
- const [serverName, _1] = server.split(':');
518
- const [_2, encodedName] = _1.split('#');
519
- const [port, pluginsStr] = _2.split('/');
520
- let plugin = null;
521
- if (pluginsStr) {
522
- try {
523
- plugin = new URLSearchParams(pluginsStr).get('plugin');
524
- } catch (e) {
525
- const err = new Error(`[ss.decodeOne] Invalid plugins: ${pluginsStr}`);
526
- err.cause = e;
527
- throw err;
528
- }
529
- }
530
- const pluginArgs = (plugin?.split(';') ?? []).reduce((acc, cur)=>{
531
- const [key, value] = cur.split('=');
532
- acc[key] = value;
533
- return acc;
534
- }, {});
535
- return {
536
- raw: sip002,
537
- type: 'ss',
538
- name: decodeURIComponent(encodedName),
539
- server: serverName,
540
- port: number(port),
541
- cipher,
542
- password,
543
- udp: true,
544
- obfs: 'obfs-local' in pluginArgs && 'obfs' in pluginArgs && (pluginArgs.obfs === 'http' || pluginArgs.obfs === 'tls') ? pluginArgs.obfs : undefined,
545
- obfsHost: 'obfs-host' in pluginArgs ? pluginArgs['obfs-host'] : undefined
546
- };
547
- }
548
- function decodeBase64Multiline(text) {
549
- return atob(text).replaceAll('\r\n', '\n').split('\n').filter(Boolean);
550
- }
551
- function decodeMultiline(text) {
552
- return decodeBase64Multiline(text).map((line)=>decodeOne(line));
553
- }
554
-
555
- var index$1 = {
556
- __proto__: null,
557
- decodeBase64Multiline: decodeBase64Multiline,
558
- decodeMultiline: decodeMultiline,
559
- decodeOne: decodeOne
560
- };
561
-
562
- function parse(line) {
563
- const url = new URL(line);
564
- // trojan://password@remote_host:remote_port
565
- const password = url.username;
566
- const server = url.hostname;
567
- const port = Number.parseInt(url.port, 10);
568
- if (Number.isNaN(port)) {
569
- throw new TypeError('invalid port: ' + url.port);
570
- }
571
- const name = decodeURIComponent(url.hash.slice(1));
572
- return {
573
- raw: line,
574
- name,
575
- type: 'trojan',
576
- server,
577
- port,
578
- password,
579
- udp: true,
580
- sni: url.searchParams.get('sni') ?? server,
581
- skipCertVerify: true
582
- };
583
- }
584
-
585
- var index = {
586
- __proto__: null,
587
- parse: parse
588
- };
589
-
590
- export { index$2 as clash, index$1 as ss, index$3 as surge, index as trojan };
1
+ import{never as e}from"foxts/guard";import{Buffer as s}from"node:buffer";let r=e=>"true"===e,t=Number,p=e=>e.split(",").map(e=>e.trim());function o(e){let s=e.indexOf("=");return -1===s?["",""]:[e.slice(0,s).trim(),e.slice(s+1).trim()]}let n=new Set(["udp-relay","tfo","reuse","skip-cert-verify","tls","vmess-aead","ws"]),a=e=>n.has(e),i=new Set(["version","download-bandwidth","port-hopping-interval","udp-port"]),d=e=>i.has(e),u=new Set([]),l=e=>u.has(e),c=new Set(["username","password","sni","encrypt-method","psk","obfs","obfs-host","uuid","alpn","block-quic","ws-path","ws-headers","port-hopping","token"]),w=e=>c.has(e),m=Symbol("unsupported"),f=e=>e.filter(Boolean).join(", ");var v={__proto__:null,decode:function(e){let[s,n]=o(e),[i,u,c,...f]=p(n),v=t(c),y=Object.fromEntries(f.map(e=>{let[s,n]=o(e);return a(s)?[s,r(n)]:d(s)?[s,t(n)]:l(s)?[s,p(n)]:w(s)?'"'===n[0]&&n.endsWith('"')||"'"===n[0]&&n.endsWith("'")?[s,n.slice(1,-1)]:[s,n]:[s,m]})),h={raw:e,name:s,server:u,port:v,tfo:y.tfo,blockQuic:y["block-quic"]};switch(i){case"snell":return{type:"snell",psk:y.psk,version:y.version,reuse:y.reuse,...h};case"ss":return{type:"ss",cipher:y["encrypt-method"],password:y.password,udp:y["udp-relay"],obfs:y.obfs,obfsHost:y["obfs-host"],obfsUri:y["obfs-uri"],udpPort:y["udp-port"],...h};case"trojan":return{type:"trojan",password:y.password,sni:y.sni,skipCertVerify:y["skip-cert-verify"],udp:y["udp-relay"],...h};case"tuic":return{type:"tuic",sni:y.sni,uuid:y.uuid,alpn:y.alpn,token:y.token,skipCertVerify:y["skip-cert-verify"],...h};case"tuic-v5":return{type:"tuic-v5",uuid:y.uuid,alpn:y.alpn,password:y.password,sni:y.sni,skipCertVerify:y["skip-cert-verify"],...h};case"socks5":return{type:"socks5",username:f[0],password:f[1],udp:y["udp-relay"],...h};case"http":return{type:"http",username:f[0],password:f[1],...h};case"vmess":return{type:"vmess",username:y.username,tls:y.tls,vmessAead:y["vmess-aead"],ws:y.ws,wsPath:y["ws-path"],wsHeaders:y["ws-headers"],skipCertVerify:y["skip-cert-verify"],udp:y["udp-relay"],sni:y.sni,...h};case"hysteria2":return{type:"hysteria2",password:y.password,skipCertVerify:y["skip-cert-verify"],downloadBandwidth:y["download-bandwidth"],portHopping:y["port-hopping"],portHoppingInterval:y["port-hopping-interval"],...h};default:throw TypeError(`Unsupported type: ${i} (surge decode)`)}},encode:function(s){let r=[s.tfo&&"tfo=true",s.blockQuic&&`block-quic=${s.blockQuic}`];switch(s.type){case"snell":return f([`${s.name} = snell, ${s.server}, ${s.port}, psk=${s.psk}, version=${s.version}, reuse=${s.reuse}`,...r]);case"ss":return f([`${s.name} = ss, ${s.server}, ${s.port}, encrypt-method=${s.cipher}, password=${s.password}`,s.udp&&"udp-relay=true",s.udpPort&&`udp-port=${s.udpPort}`,s.obfs&&`obfs=${s.obfs}`,s.obfsHost&&`obfs-host=${s.obfsHost}`,s.obfsUri&&`obfs-uri=${s.obfsUri}`,...r]);case"trojan":return f([`${s.name} = trojan, ${s.server}, ${s.port}, password=${s.password}`,s.sni&&`sni=${s.sni}`,s.skipCertVerify&&"skip-cert-verify=true",...r,s.udp&&"udp-relay=true"]);case"tuic":return f([`${s.name} = tuic, ${s.server}, ${s.port}, sni=${s.sni}, uuid=${s.uuid}, alpn=${s.alpn}, token=${s.token}`,...r]);case"socks5":return f([`${s.name} = socks5, ${s.server}, ${s.port}, ${s.username}, ${s.password}`,s.udp&&"udp-relay=true",...r]);case"http":return f([`${s.name} = http, ${s.server}, ${s.port}, ${s.username}, ${s.password}`,...r]);case"vmess":return f([`${s.name} = vmess, ${s.server}, ${s.port}`,`username=${s.username}`,`tls=${s.tls}`,`vmess-aead=${s.vmessAead}`,"ws=true",s.wsPath&&`ws-path=${"/"===s.wsPath[0]?s.wsPath:`/${s.wsPath}`}`,s.wsHeaders&&`ws-headers=${s.wsHeaders}`,`skip-cert-verify=${s.skipCertVerify}`,`tfo=${s.tfo}`,`udp-relay=${s.udp}`]);case"hysteria2":return f([`${s.name} = hysteria2, ${s.server}, ${s.port}`,`password=${s.password}`,`download-bandwidth=${s.downloadBandwidth}`,s.portHopping&&`port-hopping="${s.portHopping}"`,s.portHoppingInterval&&`port-hopping-interval=${s.portHoppingInterval}`,`skip-cert-verify=${s.skipCertVerify}`,...r]);case"tuic-v5":return f([`${s.name} = tuic-v5, ${s.server}, ${s.port}`,`password=${s.password}`,`uuid=${s.uuid}`,`alpn=${s.alpn}`,`skip-cert-verify=${s.skipCertVerify}`,`sni=${s.sni}`,...r]);default:e(s,"type (clash encode)")}}};function y(e){return e.split(",").reduce((e,s)=>{let[r,t]=s.split(":");return e[r.trim()]=t.trim(),e},{})}var h={__proto__:null,decode:function(e){if(!("type"in e)||"string"!=typeof e.type)throw TypeError("Missing or invalid type field");let s=JSON.stringify(e);switch(e.type){case"http":return{type:"http",name:e.name,server:e.server,port:Number(e.port),username:e.username,password:e.password,raw:s};case"ss":return{type:"ss",name:e.name,server:e.server,port:Number(e.port),cipher:e.cipher,password:e.password,udp:e.udp||!1,obfs:"obfs"===e.plugin?e["plugin-opts"].mode:void 0,raw:s};case"socks5":return{type:"socks5",name:e.name,server:e.server,port:Number(e.port),username:e.username,password:e.password,udp:e.udp||!1,raw:s};case"trojan":return{type:"trojan",name:e.name,server:e.server,port:Number(e.port),password:e.password,sni:e.sni,skipCertVerify:e["skip-cert-verify"]||!1,udp:e.udp||!1,raw:s};case"vmess":return{type:"vmess",name:e.name,server:e.server,port:Number(e.port),username:e.uuid,vmessAead:1===e.alterId||"1"===e.alterId,sni:e.servername,ws:"ws"===e.network,wsPath:e["ws-path"],wsHeaders:e["ws-headers"]?Object.entries(e["ws-headers"]).map(([e,s])=>`${e}:${s}`).join(", "):void 0,tls:e.tls||!1,udp:e.udp??!0,raw:s,skipCertVerify:e["skip-cert-verify"]||!1};default:throw TypeError(`Unsupported type: ${e.type} (clash decode)`)}},encode:function(e){let s={tfo:e.tfo};switch(e.type){case"ss":return{name:e.name,type:"ss",server:e.server,port:e.port,cipher:e.cipher,password:e.password,udp:e.udp,...e.obfs?{plugin:"obfs","plugin-opts":{mode:e.obfs,host:e.obfsHost,uri:e.obfsUri}}:{},...s};case"trojan":return{name:e.name,type:"trojan",server:e.server,port:e.port,password:e.password,sni:e.sni,"skip-cert-verify":e.skipCertVerify,udp:e.udp,...s};case"tuic":case"tuic-v5":return{name:e.name,type:"tuic",server:e.server,port:e.port,sni:e.sni,uuid:e.uuid,alpn:e.alpn.split(",").map(e=>e.trim()),..."tuic"===e.type?{token:e.token}:{password:e.password},"skip-cert-verify":e.skipCertVerify,udp:!0,...s};case"socks5":return{name:e.name,type:"socks5",server:e.server,port:e.port,username:e.username,password:e.password,udp:e.udp,...s};case"http":return{name:e.name,type:"http",server:e.server,port:e.port,username:e.username,password:e.password,...s};case"vmess":return{alterId:e.vmessAead?0:void 0,tls:e.tls,udp:e.udp,uuid:e.username,name:e.name,servername:e.sni,"ws-path":e.wsPath,server:e.server,"ws-headers":e.wsHeaders?y(e.wsHeaders):void 0,cipher:"auto","ws-opts":{path:e.wsPath,headers:e.wsHeaders?y(e.wsHeaders):void 0},type:"vmess",port:e.port,network:e.ws?"ws":"tcp"};case"hysteria2":return{name:e.name,type:"hysteria2",server:e.server,port:e.port,ports:e.portHopping,password:e.password,down:e.downloadBandwidth+" Mbps","skip-cert-verify":e.skipCertVerify};default:throw TypeError(`Unsupported type: ${e.type} (clash encode)`)}}};function $(e){let s,r;let[p,o]=e.split("://");if("ss"!==p)throw Error(`[ss.decodeOne] Unsupported type: ${p}`);let[n,a]=o.split("@");n.includes(":")?[s,r]=n.split(":"):[s,r]=atob(n).split(":");let[i,d]=a.split(":"),[u,l]=d.split("#"),[c,w]=u.split("/"),m=null;if(w)try{m=new URLSearchParams(w).get("plugin")}catch(s){let e=Error(`[ss.decodeOne] Invalid plugins: ${w}`);throw e.cause=s,e}let f=(m?.split(";")??[]).reduce((e,s)=>{let[r,t]=s.split("=");return e[r]=t,e},{});return{raw:e,type:"ss",name:decodeURIComponent(l),server:i,port:t(c),cipher:s,password:r,udp:!0,obfs:"obfs-local"in f&&"obfs"in f&&("http"===f.obfs||"tls"===f.obfs)?f.obfs:void 0,obfsHost:"obfs-host"in f?f["obfs-host"]:void 0}}function b(e){return atob(e).replaceAll("\r\n","\n").split("\n").filter(Boolean)}var k={__proto__:null,decodeBase64Multiline:b,decodeMultiline:function(e){return b(e).map(e=>$(e))},decodeOne:$},g={__proto__:null,parse:function(e){let s=new URL(e),r=s.username,t=s.hostname,p=Number.parseInt(s.port,10);if(Number.isNaN(p))throw TypeError("invalid port: "+s.port);return{raw:e,name:decodeURIComponent(s.hash.slice(1)),type:"trojan",server:t,port:p,password:r,udp:!0,sni:s.searchParams.get("sni")??t,skipCertVerify:!0}}};let H=new TextDecoder;var _={__proto__:null,parse:function(e){let r=JSON.parse(H.decode(s.from(e.slice(8),"base64"))),t=r.ps,p=r.path;return{raw:e,name:t,server:r.add,port:Number.parseInt(r.port,10),type:"vmess",username:r.id,tls:r.tls,vmessAead:"0"===r.aid,sni:r.sni,ws:"ws"===r.net,wsPath:"/"===p[0]?p:`/${p}`,wsHeaders:r.host?`Host:${r.host}`:void 0,skipCertVerify:!0,udp:!0}}};export{h as clash,k as ss,v as surge,g as trojan,_ as vmess};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodelistparser",
3
- "version": "0.3.1",
3
+ "version": "1.0.1",
4
4
  "description": "Surge / Mihomo (Clash.Meta) nodelist / proxy provider parser and generator.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "scripts": {
55
55
  "lint": "eslint --format=sukka .",
56
- "build": "bunchee",
56
+ "build": "bunchee --minify --no-sourcemap",
57
57
  "test": "mocha --require @swc-node/register src/*.test.ts src/**/*.test.ts",
58
58
  "prerelease": "pnpm run lint && pnpm run build",
59
59
  "release": "bumpp -r --all --commit \"release: %s\" --tag \"%s\""