nodelistparser 0.3.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/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");const r=e=>"true"===e,s=Number,t=e=>e.split(",").map(e=>e.trim());function p(e){let r=e.indexOf("=");return -1===r?["",""]:[e.slice(0,r).trim(),e.slice(r+1).trim()]}const o=new Set(["udp-relay","tfo","reuse","skip-cert-verify","tls","vmess-aead","ws"]),n=e=>o.has(e),a=new Set(["version","download-bandwidth","port-hopping-interval","udp-port"]),i=e=>a.has(e),u=new Set([]),d=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"]),l=e=>c.has(e),w=Symbol("unsupported"),m=e=>e.filter(Boolean).join(", ");function f(e){return e.split(",").reduce((e,r)=>{let[s,t]=r.split(":");return e[s.trim()]=t.trim(),e},{})}function v(e){let r,t;let[p,o]=e.split("://");if("ss"!==p)throw Error(`[ss.decodeOne] Unsupported type: ${p}`);let[n,a]=o.split("@");n.includes(":")?[r,t]=n.split(":"):[r,t]=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(r){let e=Error(`[ss.decodeOne] Invalid plugins: ${w}`);throw e.cause=r,e}let f=(m?.split(";")??[]).reduce((e,r)=>{let[s,t]=r.split("=");return e[s]=t,e},{});return{raw:e,type:"ss",name:decodeURIComponent(c),server:i,port:s(l),cipher:r,password:t,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 y(e){return atob(e).replaceAll("\r\n","\n").split("\n").filter(Boolean)}exports.clash={__proto__:null,decode:function(e){if(!("type"in e)||"string"!=typeof e.type)throw TypeError("Missing or invalid type field");let r=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:r};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:r};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:r};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:r};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,r])=>`${e}:${r}`).join(", "):void 0,tls:e.tls||!1,udp:e.udp??!0,raw:r,skipCertVerify:e["skip-cert-verify"]||!1};default:throw TypeError(`Unsupported type: ${e.type} (clash decode)`)}},encode:function(e){let r={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}}:{},...r};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,...r};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,...r};case"socks5":return{name:e.name,type:"socks5",server:e.server,port:e.port,username:e.username,password:e.password,udp:e.udp,...r};case"http":return{name:e.name,type:"http",server:e.server,port:e.port,username:e.username,password:e.password,...r};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?f(e.wsHeaders):void 0,cipher:"auto","ws-opts":{path:e.wsPath,headers:e.wsHeaders?f(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:y,decodeMultiline:function(e){return y(e).map(e=>v(e))},decodeOne:v},exports.surge={__proto__:null,decode:function(e){let[o,a]=p(e),[u,c,m,...f]=t(a),v=s(m),y=Object.fromEntries(f.map(e=>{let[o,a]=p(e);return n(o)?[o,r(a)]:i(o)?[o,s(a)]:d(o)?[o,t(a)]:l(o)?'"'===a[0]&&a.endsWith('"')||"'"===a[0]&&a.endsWith("'")?[o,a.slice(1,-1)]:[o,a]:[o,w]})),h={raw:e,name:o,server:c,port:v,tfo:y.tfo,blockQuic:y["block-quic"]};switch(u){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: ${u} (surge decode)`)}},encode:function(r){let s=[r.tfo&&"tfo=true",r.blockQuic&&`block-quic=${r.blockQuic}`];switch(r.type){case"snell":return m([`${r.name} = snell, ${r.server}, ${r.port}, psk=${r.psk}, version=${r.version}, reuse=${r.reuse}`,...s]);case"ss":return m([`${r.name} = ss, ${r.server}, ${r.port}, encrypt-method=${r.cipher}, password=${r.password}`,r.udp&&"udp-relay=true",r.udpPort&&`udp-port=${r.udpPort}`,r.obfs&&`obfs=${r.obfs}`,r.obfsHost&&`obfs-host=${r.obfsHost}`,r.obfsUri&&`obfs-uri=${r.obfsUri}`,...s]);case"trojan":return m([`${r.name} = trojan, ${r.server}, ${r.port}, password=${r.password}`,r.sni&&`sni=${r.sni}`,r.skipCertVerify&&"skip-cert-verify=true",...s,r.udp&&"udp-relay=true"]);case"tuic":return m([`${r.name} = tuic, ${r.server}, ${r.port}, sni=${r.sni}, uuid=${r.uuid}, alpn=${r.alpn}, token=${r.token}`,...s]);case"socks5":return m([`${r.name} = socks5, ${r.server}, ${r.port}, ${r.username}, ${r.password}`,r.udp&&"udp-relay=true",...s]);case"http":return m([`${r.name} = http, ${r.server}, ${r.port}, ${r.username}, ${r.password}`,...s]);case"vmess":return m([`${r.name} = vmess, ${r.server}, ${r.port}`,`username=${r.username}`,`tls=${r.tls}`,`vmess-aead=${r.vmessAead}`,"ws=true",r.wsPath&&`ws-path=${"/"===r.wsPath[0]?r.wsPath:`/${r.wsPath}`}`,r.wsHeaders&&`ws-headers=${r.wsHeaders}`,`skip-cert-verify=${r.skipCertVerify}`,`tfo=${r.tfo}`,`udp-relay=${r.udp}`]);case"hysteria2":return m([`${r.name} = hysteria2, ${r.server}, ${r.port}`,`password=${r.password}`,`download-bandwidth=${r.downloadBandwidth}`,r.portHopping&&`port-hopping="${r.portHopping}"`,r.portHoppingInterval&&`port-hopping-interval=${r.portHoppingInterval}`,`skip-cert-verify=${r.skipCertVerify}`,...s]);case"tuic-v5":return m([`${r.name} = tuic-v5, ${r.server}, ${r.port}`,`password=${r.password}`,`uuid=${r.uuid}`,`alpn=${r.alpn}`,`skip-cert-verify=${r.skipCertVerify}`,`sni=${r.sni}`,...s]);default:e.never(r,"type (clash encode)")}}},exports.trojan={__proto__:null,parse:function(e){let r=new URL(e),s=r.username,t=r.hostname,p=Number.parseInt(r.port,10);if(Number.isNaN(p))throw TypeError("invalid port: "+r.port);return{raw:e,name:decodeURIComponent(r.hash.slice(1)),type:"trojan",server:t,port:p,password:s,udp:!0,sni:r.searchParams.get("sni")??t,skipCertVerify:!0}}};
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";let r=e=>"true"===e,s=Number,t=e=>e.split(",").map(e=>e.trim());function p(e){let r=e.indexOf("=");return -1===r?["",""]:[e.slice(0,r).trim(),e.slice(r+1).trim()]}let o=new Set(["udp-relay","tfo","reuse","skip-cert-verify","tls","vmess-aead","ws"]),n=e=>o.has(e),a=new Set(["version","download-bandwidth","port-hopping-interval","udp-port"]),i=e=>a.has(e),u=new Set([]),d=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"]),l=e=>c.has(e),w=Symbol("unsupported"),m=e=>e.filter(Boolean).join(", ");var f={__proto__:null,decode:function(e){let[o,a]=p(e),[u,c,m,...f]=t(a),v=s(m),y=Object.fromEntries(f.map(e=>{let[o,a]=p(e);return n(o)?[o,r(a)]:i(o)?[o,s(a)]:d(o)?[o,t(a)]:l(o)?'"'===a[0]&&a.endsWith('"')||"'"===a[0]&&a.endsWith("'")?[o,a.slice(1,-1)]:[o,a]:[o,w]})),h={raw:e,name:o,server:c,port:v,tfo:y.tfo,blockQuic:y["block-quic"]};switch(u){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: ${u} (surge decode)`)}},encode:function(r){let s=[r.tfo&&"tfo=true",r.blockQuic&&`block-quic=${r.blockQuic}`];switch(r.type){case"snell":return m([`${r.name} = snell, ${r.server}, ${r.port}, psk=${r.psk}, version=${r.version}, reuse=${r.reuse}`,...s]);case"ss":return m([`${r.name} = ss, ${r.server}, ${r.port}, encrypt-method=${r.cipher}, password=${r.password}`,r.udp&&"udp-relay=true",r.udpPort&&`udp-port=${r.udpPort}`,r.obfs&&`obfs=${r.obfs}`,r.obfsHost&&`obfs-host=${r.obfsHost}`,r.obfsUri&&`obfs-uri=${r.obfsUri}`,...s]);case"trojan":return m([`${r.name} = trojan, ${r.server}, ${r.port}, password=${r.password}`,r.sni&&`sni=${r.sni}`,r.skipCertVerify&&"skip-cert-verify=true",...s,r.udp&&"udp-relay=true"]);case"tuic":return m([`${r.name} = tuic, ${r.server}, ${r.port}, sni=${r.sni}, uuid=${r.uuid}, alpn=${r.alpn}, token=${r.token}`,...s]);case"socks5":return m([`${r.name} = socks5, ${r.server}, ${r.port}, ${r.username}, ${r.password}`,r.udp&&"udp-relay=true",...s]);case"http":return m([`${r.name} = http, ${r.server}, ${r.port}, ${r.username}, ${r.password}`,...s]);case"vmess":return m([`${r.name} = vmess, ${r.server}, ${r.port}`,`username=${r.username}`,`tls=${r.tls}`,`vmess-aead=${r.vmessAead}`,"ws=true",r.wsPath&&`ws-path=${"/"===r.wsPath[0]?r.wsPath:`/${r.wsPath}`}`,r.wsHeaders&&`ws-headers=${r.wsHeaders}`,`skip-cert-verify=${r.skipCertVerify}`,`tfo=${r.tfo}`,`udp-relay=${r.udp}`]);case"hysteria2":return m([`${r.name} = hysteria2, ${r.server}, ${r.port}`,`password=${r.password}`,`download-bandwidth=${r.downloadBandwidth}`,r.portHopping&&`port-hopping="${r.portHopping}"`,r.portHoppingInterval&&`port-hopping-interval=${r.portHoppingInterval}`,`skip-cert-verify=${r.skipCertVerify}`,...s]);case"tuic-v5":return m([`${r.name} = tuic-v5, ${r.server}, ${r.port}`,`password=${r.password}`,`uuid=${r.uuid}`,`alpn=${r.alpn}`,`skip-cert-verify=${r.skipCertVerify}`,`sni=${r.sni}`,...s]);default:e(r,"type (clash encode)")}}};function v(e){return e.split(",").reduce((e,r)=>{let[s,t]=r.split(":");return e[s.trim()]=t.trim(),e},{})}var y={__proto__:null,decode:function(e){if(!("type"in e)||"string"!=typeof e.type)throw TypeError("Missing or invalid type field");let r=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:r};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:r};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:r};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:r};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,r])=>`${e}:${r}`).join(", "):void 0,tls:e.tls||!1,udp:e.udp??!0,raw:r,skipCertVerify:e["skip-cert-verify"]||!1};default:throw TypeError(`Unsupported type: ${e.type} (clash decode)`)}},encode:function(e){let r={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}}:{},...r};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,...r};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,...r};case"socks5":return{name:e.name,type:"socks5",server:e.server,port:e.port,username:e.username,password:e.password,udp:e.udp,...r};case"http":return{name:e.name,type:"http",server:e.server,port:e.port,username:e.username,password:e.password,...r};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)`)}}};function h(e){let r,t;let[p,o]=e.split("://");if("ss"!==p)throw Error(`[ss.decodeOne] Unsupported type: ${p}`);let[n,a]=o.split("@");n.includes(":")?[r,t]=n.split(":"):[r,t]=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(r){let e=Error(`[ss.decodeOne] Invalid plugins: ${w}`);throw e.cause=r,e}let f=(m?.split(";")??[]).reduce((e,r)=>{let[s,t]=r.split("=");return e[s]=t,e},{});return{raw:e,type:"ss",name:decodeURIComponent(c),server:i,port:s(l),cipher:r,password:t,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 $(e){return atob(e).replaceAll("\r\n","\n").split("\n").filter(Boolean)}var k={__proto__:null,decodeBase64Multiline:$,decodeMultiline:function(e){return $(e).map(e=>h(e))},decodeOne:h},b={__proto__:null,parse:function(e){let r=new URL(e),s=r.username,t=r.hostname,p=Number.parseInt(r.port,10);if(Number.isNaN(p))throw TypeError("invalid port: "+r.port);return{raw:e,name:decodeURIComponent(r.hash.slice(1)),type:"trojan",server:t,port:p,password:s,udp:!0,sni:r.searchParams.get("sni")??t,skipCertVerify:!0}}};export{y as clash,k as ss,f as surge,b as trojan};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodelistparser",
3
- "version": "0.3.1",
3
+ "version": "1.0.0",
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\""