nodelistparser 0.1.3 → 0.2.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.
@@ -92,11 +92,11 @@ type SupportedConfig = HttpProxyConfig | SnellConfig | TrojanConfig | ShadowSock
92
92
  declare function decode$1(raw: string): SupportedConfig;
93
93
  declare function encode$1(config: SupportedConfig): string;
94
94
 
95
- declare namespace index$2 {
95
+ declare namespace index$3 {
96
96
  export { decode$1 as decode, encode$1 as encode };
97
97
  }
98
98
 
99
- declare function decode(config: any): SupportedConfig;
99
+ declare function decode(config: Record<string, any>): SupportedConfig;
100
100
  declare function encode(config: SupportedConfig): {
101
101
  tfo: boolean | undefined;
102
102
  plugin?: string | undefined;
@@ -253,21 +253,28 @@ declare function encode(config: SupportedConfig): {
253
253
  network?: undefined;
254
254
  };
255
255
 
256
- declare const index$1_decode: typeof decode;
257
- declare const index$1_encode: typeof encode;
258
- declare namespace index$1 {
259
- export { index$1_decode as decode, index$1_encode as encode };
256
+ declare const index$2_decode: typeof decode;
257
+ declare const index$2_encode: typeof encode;
258
+ declare namespace index$2 {
259
+ export { index$2_decode as decode, index$2_encode as encode };
260
260
  }
261
261
 
262
262
  declare function decodeOne(sip002: string): ShadowSocksConfig;
263
263
  declare function decodeBase64Multiline(text: string): string[];
264
264
  declare function decodeMultiline(text: string): ShadowSocksConfig[];
265
265
 
266
- declare const index_decodeBase64Multiline: typeof decodeBase64Multiline;
267
- declare const index_decodeMultiline: typeof decodeMultiline;
268
- declare const index_decodeOne: typeof decodeOne;
266
+ declare const index$1_decodeBase64Multiline: typeof decodeBase64Multiline;
267
+ declare const index$1_decodeMultiline: typeof decodeMultiline;
268
+ declare const index$1_decodeOne: typeof decodeOne;
269
+ declare namespace index$1 {
270
+ export { index$1_decodeBase64Multiline as decodeBase64Multiline, index$1_decodeMultiline as decodeMultiline, index$1_decodeOne as decodeOne };
271
+ }
272
+
273
+ declare function parse(line: string): TrojanConfig;
274
+
275
+ declare const index_parse: typeof parse;
269
276
  declare namespace index {
270
- export { index_decodeBase64Multiline as decodeBase64Multiline, index_decodeMultiline as decodeMultiline, index_decodeOne as decodeOne };
277
+ export { index_parse as parse };
271
278
  }
272
279
 
273
- export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type VmessConfig, index$1 as clash, index as ss, index$2 as surge };
280
+ export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type VmessConfig, index$2 as clash, index$1 as ss, index$3 as surge, index as trojan };
package/dist/cjs/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ var guard = require('foxts/guard');
2
+
1
3
  const boolean = (text)=>text === 'true';
2
4
  const number = Number;
3
5
  const comma = (text)=>text.split(',').map((piece)=>piece.trim());
@@ -198,9 +200,6 @@ function decode$1(raw) {
198
200
  // name, type, server, port, restDetails
199
201
  // });
200
202
  }
201
- function assertNever(value, msg) {
202
- throw new TypeError(`Unsupported type: ${msg}`);
203
- }
204
203
  const joinString = (arr)=>arr.filter(Boolean).join(', ');
205
204
  function encode$1(config) {
206
205
  const shared = [
@@ -272,11 +271,11 @@ function encode$1(config) {
272
271
  ...shared
273
272
  ]);
274
273
  default:
275
- assertNever(config, `Unsupported type: ${config.type} (clash encode)`);
274
+ guard.never(config, 'type (clash encode)');
276
275
  }
277
276
  }
278
277
 
279
- var index$2 = {
278
+ var index$3 = {
280
279
  __proto__: null,
281
280
  decode: decode$1,
282
281
  encode: encode$1
@@ -468,7 +467,7 @@ function parseStringToObject(input) {
468
467
  }, {});
469
468
  }
470
469
 
471
- var index$1 = {
470
+ var index$2 = {
472
471
  __proto__: null,
473
472
  decode: decode,
474
473
  encode: encode
@@ -489,8 +488,22 @@ function decodeOne(sip002) {
489
488
  }
490
489
  const [serverName, _1] = server.split(':');
491
490
  const [_2, encodedName] = _1.split('#');
492
- const [port, _plugins] = _2.split('/');
493
- // TODO: implement plugin parsing
491
+ const [port, pluginsStr] = _2.split('/');
492
+ let plugin = null;
493
+ if (pluginsStr) {
494
+ try {
495
+ plugin = new URLSearchParams(pluginsStr).get('plugin');
496
+ } catch (e) {
497
+ const err = new Error(`[ss.decodeOne] Invalid plugins: ${pluginsStr}`);
498
+ err.cause = e;
499
+ throw err;
500
+ }
501
+ }
502
+ const pluginArgs = (plugin?.split(';') ?? []).reduce((acc, cur)=>{
503
+ const [key, value] = cur.split('=');
504
+ acc[key] = value;
505
+ return acc;
506
+ }, {});
494
507
  return {
495
508
  raw: sip002,
496
509
  type: 'ss',
@@ -499,7 +512,9 @@ function decodeOne(sip002) {
499
512
  port: number(port),
500
513
  cipher,
501
514
  password,
502
- udp: true
515
+ udp: true,
516
+ obfs: 'obfs-local' in pluginArgs && 'obfs' in pluginArgs && (pluginArgs.obfs === 'http' || pluginArgs.obfs === 'tls') ? pluginArgs.obfs : undefined,
517
+ obfsHost: 'obfs-host' in pluginArgs ? pluginArgs['obfs-host'] : undefined
503
518
  };
504
519
  }
505
520
  function decodeBase64Multiline(text) {
@@ -509,13 +524,42 @@ function decodeMultiline(text) {
509
524
  return decodeBase64Multiline(text).map((line)=>decodeOne(line));
510
525
  }
511
526
 
512
- var index = {
527
+ var index$1 = {
513
528
  __proto__: null,
514
529
  decodeBase64Multiline: decodeBase64Multiline,
515
530
  decodeMultiline: decodeMultiline,
516
531
  decodeOne: decodeOne
517
532
  };
518
533
 
519
- exports.clash = index$1;
520
- exports.ss = index;
521
- exports.surge = index$2;
534
+ function parse(line) {
535
+ const url = new URL(line);
536
+ // trojan://password@remote_host:remote_port
537
+ const password = url.username;
538
+ const server = url.hostname;
539
+ const port = Number.parseInt(url.port, 10);
540
+ if (Number.isNaN(port)) {
541
+ throw new TypeError('invalid port: ' + url.port);
542
+ }
543
+ const name = decodeURIComponent(url.hash.slice(1));
544
+ return {
545
+ raw: line,
546
+ name,
547
+ type: 'trojan',
548
+ server,
549
+ port,
550
+ password,
551
+ udp: true,
552
+ sni: url.searchParams.get('sni') ?? server,
553
+ skipCertVerify: true
554
+ };
555
+ }
556
+
557
+ var index = {
558
+ __proto__: null,
559
+ parse: parse
560
+ };
561
+
562
+ exports.clash = index$2;
563
+ exports.ss = index$1;
564
+ exports.surge = index$3;
565
+ exports.trojan = index;
@@ -92,11 +92,11 @@ type SupportedConfig = HttpProxyConfig | SnellConfig | TrojanConfig | ShadowSock
92
92
  declare function decode$1(raw: string): SupportedConfig;
93
93
  declare function encode$1(config: SupportedConfig): string;
94
94
 
95
- declare namespace index$2 {
95
+ declare namespace index$3 {
96
96
  export { decode$1 as decode, encode$1 as encode };
97
97
  }
98
98
 
99
- declare function decode(config: any): SupportedConfig;
99
+ declare function decode(config: Record<string, any>): SupportedConfig;
100
100
  declare function encode(config: SupportedConfig): {
101
101
  tfo: boolean | undefined;
102
102
  plugin?: string | undefined;
@@ -253,21 +253,28 @@ declare function encode(config: SupportedConfig): {
253
253
  network?: undefined;
254
254
  };
255
255
 
256
- declare const index$1_decode: typeof decode;
257
- declare const index$1_encode: typeof encode;
258
- declare namespace index$1 {
259
- export { index$1_decode as decode, index$1_encode as encode };
256
+ declare const index$2_decode: typeof decode;
257
+ declare const index$2_encode: typeof encode;
258
+ declare namespace index$2 {
259
+ export { index$2_decode as decode, index$2_encode as encode };
260
260
  }
261
261
 
262
262
  declare function decodeOne(sip002: string): ShadowSocksConfig;
263
263
  declare function decodeBase64Multiline(text: string): string[];
264
264
  declare function decodeMultiline(text: string): ShadowSocksConfig[];
265
265
 
266
- declare const index_decodeBase64Multiline: typeof decodeBase64Multiline;
267
- declare const index_decodeMultiline: typeof decodeMultiline;
268
- declare const index_decodeOne: typeof decodeOne;
266
+ declare const index$1_decodeBase64Multiline: typeof decodeBase64Multiline;
267
+ declare const index$1_decodeMultiline: typeof decodeMultiline;
268
+ declare const index$1_decodeOne: typeof decodeOne;
269
+ declare namespace index$1 {
270
+ export { index$1_decodeBase64Multiline as decodeBase64Multiline, index$1_decodeMultiline as decodeMultiline, index$1_decodeOne as decodeOne };
271
+ }
272
+
273
+ declare function parse(line: string): TrojanConfig;
274
+
275
+ declare const index_parse: typeof parse;
269
276
  declare namespace index {
270
- export { index_decodeBase64Multiline as decodeBase64Multiline, index_decodeMultiline as decodeMultiline, index_decodeOne as decodeOne };
277
+ export { index_parse as parse };
271
278
  }
272
279
 
273
- export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type VmessConfig, index$1 as clash, index as ss, index$2 as surge };
280
+ export { type HttpProxyConfig, type Hysteria2Config, type ShadowSocksConfig, type SharedConfigBase, type SnellConfig, type Socks5Config, type SupportedConfig, type TrojanBasicConfig, type TrojanConfig, type TuicConfig, type VmessConfig, index$2 as clash, index$1 as ss, index$3 as surge, index as trojan };
package/dist/es/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { never } from 'foxts/guard';
2
+
1
3
  const boolean = (text)=>text === 'true';
2
4
  const number = Number;
3
5
  const comma = (text)=>text.split(',').map((piece)=>piece.trim());
@@ -198,9 +200,6 @@ function decode$1(raw) {
198
200
  // name, type, server, port, restDetails
199
201
  // });
200
202
  }
201
- function assertNever(value, msg) {
202
- throw new TypeError(`Unsupported type: ${msg}`);
203
- }
204
203
  const joinString = (arr)=>arr.filter(Boolean).join(', ');
205
204
  function encode$1(config) {
206
205
  const shared = [
@@ -272,11 +271,11 @@ function encode$1(config) {
272
271
  ...shared
273
272
  ]);
274
273
  default:
275
- assertNever(config, `Unsupported type: ${config.type} (clash encode)`);
274
+ never(config, 'type (clash encode)');
276
275
  }
277
276
  }
278
277
 
279
- var index$2 = {
278
+ var index$3 = {
280
279
  __proto__: null,
281
280
  decode: decode$1,
282
281
  encode: encode$1
@@ -468,7 +467,7 @@ function parseStringToObject(input) {
468
467
  }, {});
469
468
  }
470
469
 
471
- var index$1 = {
470
+ var index$2 = {
472
471
  __proto__: null,
473
472
  decode: decode,
474
473
  encode: encode
@@ -489,8 +488,22 @@ function decodeOne(sip002) {
489
488
  }
490
489
  const [serverName, _1] = server.split(':');
491
490
  const [_2, encodedName] = _1.split('#');
492
- const [port, _plugins] = _2.split('/');
493
- // TODO: implement plugin parsing
491
+ const [port, pluginsStr] = _2.split('/');
492
+ let plugin = null;
493
+ if (pluginsStr) {
494
+ try {
495
+ plugin = new URLSearchParams(pluginsStr).get('plugin');
496
+ } catch (e) {
497
+ const err = new Error(`[ss.decodeOne] Invalid plugins: ${pluginsStr}`);
498
+ err.cause = e;
499
+ throw err;
500
+ }
501
+ }
502
+ const pluginArgs = (plugin?.split(';') ?? []).reduce((acc, cur)=>{
503
+ const [key, value] = cur.split('=');
504
+ acc[key] = value;
505
+ return acc;
506
+ }, {});
494
507
  return {
495
508
  raw: sip002,
496
509
  type: 'ss',
@@ -499,7 +512,9 @@ function decodeOne(sip002) {
499
512
  port: number(port),
500
513
  cipher,
501
514
  password,
502
- udp: true
515
+ udp: true,
516
+ obfs: 'obfs-local' in pluginArgs && 'obfs' in pluginArgs && (pluginArgs.obfs === 'http' || pluginArgs.obfs === 'tls') ? pluginArgs.obfs : undefined,
517
+ obfsHost: 'obfs-host' in pluginArgs ? pluginArgs['obfs-host'] : undefined
503
518
  };
504
519
  }
505
520
  function decodeBase64Multiline(text) {
@@ -509,11 +524,39 @@ function decodeMultiline(text) {
509
524
  return decodeBase64Multiline(text).map((line)=>decodeOne(line));
510
525
  }
511
526
 
512
- var index = {
527
+ var index$1 = {
513
528
  __proto__: null,
514
529
  decodeBase64Multiline: decodeBase64Multiline,
515
530
  decodeMultiline: decodeMultiline,
516
531
  decodeOne: decodeOne
517
532
  };
518
533
 
519
- export { index$1 as clash, index as ss, index$2 as surge };
534
+ function parse(line) {
535
+ const url = new URL(line);
536
+ // trojan://password@remote_host:remote_port
537
+ const password = url.username;
538
+ const server = url.hostname;
539
+ const port = Number.parseInt(url.port, 10);
540
+ if (Number.isNaN(port)) {
541
+ throw new TypeError('invalid port: ' + url.port);
542
+ }
543
+ const name = decodeURIComponent(url.hash.slice(1));
544
+ return {
545
+ raw: line,
546
+ name,
547
+ type: 'trojan',
548
+ server,
549
+ port,
550
+ password,
551
+ udp: true,
552
+ sni: url.searchParams.get('sni') ?? server,
553
+ skipCertVerify: true
554
+ };
555
+ }
556
+
557
+ var index = {
558
+ __proto__: null,
559
+ parse: parse
560
+ };
561
+
562
+ export { index$2 as clash, index$1 as ss, index$3 as surge, index as trojan };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodelistparser",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Surge / Mihomo (Clash.Meta) nodelist / proxy provider parser and generator.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,19 +33,23 @@
33
33
  ],
34
34
  "author": "Sukka <https://skk.moe>",
35
35
  "license": "MIT",
36
+ "dependencies": {
37
+ "foxts": "^1.5.1"
38
+ },
36
39
  "devDependencies": {
37
- "@eslint-sukka/node": "^6.8.1",
40
+ "@eslint-sukka/node": "^6.16.1",
38
41
  "@swc-node/register": "^1.10.9",
39
- "@types/mocha": "^10.0.9",
40
- "@types/node": "^22.8.1",
41
- "bumpp": "^9.7.1",
42
- "bunchee": "^5.5.1",
43
- "eslint": "^9.13.0",
44
- "eslint-config-sukka": "^6.8.1",
45
- "eslint-formatter-sukka": "^6.8.1",
42
+ "@swc/core": "^1.11.8",
43
+ "@types/mocha": "^10.0.10",
44
+ "@types/node": "^22.13.10",
45
+ "bumpp": "^10.0.3",
46
+ "bunchee": "^6.4.0",
47
+ "eslint": "^9.22.0",
48
+ "eslint-config-sukka": "^6.16.1",
49
+ "eslint-formatter-sukka": "^6.16.1",
46
50
  "expect": "^29.7.0",
47
- "mocha": "^10.7.3",
48
- "typescript": "^5.6.3"
51
+ "mocha": "^11.1.0",
52
+ "typescript": "^5.8.2"
49
53
  },
50
54
  "scripts": {
51
55
  "lint": "eslint --format=sukka .",