@lionad/port-key 0.1.6 → 0.3.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/README.md CHANGED
@@ -8,6 +8,11 @@
8
8
  <strong>PortKey:A Simple, Practical Port Naming Strategy</strong>
9
9
  </p>
10
10
 
11
+ <p align="center">
12
+ <!-- LANGUAGES=("cn" "es" "fr" "de" "ja" "ko" "ru" "ar" "pt" "it") -->
13
+ <a href="./docs/README.cn.md">中文</a> | <a href="./docs/README.es.md">Español</a> | <a href="./docs/README.fr.md">Français</a> | <a href="./docs/README.de.md">Deutsch</a> | <a href="./docs/README.ja.md">日本語</a> | <a href="./docs/README.ko.md">한국어</a> | <a href="./docs/README.ru.md">Русский</a> | <a href="./docs/README.ar.md">العربية</a> | <a href="./docs/README.pt.md">Português</a> | <a href="./docs/README.it.md">Italiano</a>
14
+ </p>
15
+
11
16
  ## Brief
12
17
 
13
18
  Generate ports with a letter-to-number keyboard mapping
@@ -25,7 +30,7 @@ For example, I have more than ten Nuxt apps on my machine. If they all default t
25
30
 
26
31
  Instead of picking random numbers, map the **project name to numbers based on the keyboard**, so the port is *readable* and *memorable*.
27
32
 
28
- As long as the result is within the valid port range (**0–65535**) and doesn’t hit reserved/system ports, you can just use it.
33
+ As long as the result is within the valid port range (**1024–65535**) and doesn’t hit reserved/system ports, you can just use it.
29
34
 
30
35
  More specifically: using a standard QWERTY keyboard, map each letter to a single digit based on its **row/column position**.
31
36
 
@@ -53,23 +58,45 @@ If a project needs multiple ports (frontend, backend, database, etc.), pick **on
53
58
 
54
59
  ### Valid port range
55
60
 
56
- - Ports must be within **0–65535**.
57
- - For custom services, it’s usually best to use **1024–49151** (non-reserved) or **49152–65535** (private/dynamic).
58
- - As long as the mapped number stays under the limit, it’s valid.
61
+ - Ports must be within **1024–65535** (System ports 0-1023 are blocked).
62
+ - **System Ports (0-1023)**: Assigned by IETF. Strictly blocked.
63
+ - **User Ports (1024-49151)**: Assigned by IANA. Use with caution as they might conflict with registered services.
64
+ - **Dynamic/Private Ports (49152-65535)**: Not assigned. Safest for private or dynamic use.
59
65
 
60
66
  ---
61
67
 
62
68
  ## How to use
63
69
 
70
+ Simple command:
71
+
72
+ ```sh
73
+ npx -y @lionad/port-key <your-project-name>
74
+ ```
75
+
76
+ Or you want a stdio MCP server:
77
+
78
+ ```sh
79
+ npx -y @lionad/port-key-mcp
64
80
  ```
65
- npx @lionad/port-key <your-project-name>
81
+
82
+ ```json
83
+ {
84
+ "mcpServers": {
85
+ "port-key": {
86
+ "command": "npx",
87
+ "args": ["@lionad/port-key-mcp"]
88
+ }
89
+ }
90
+ }
66
91
  ```
67
92
 
93
+
68
94
  ### CLI options
69
95
 
70
96
  - `-m, --map <object>`: custom mapping (JSON or JS-like object literal)
71
97
  - `--lang <code>`: output language (currently only `en` and `cn`, default: `cn`)
72
98
  - `-d, --digits <count>`: preferred digit count for port (4 or 5, default: 4)
99
+ - `--padding-zero <true|false>`: Pad short ports with zero (default: true). e.g. "air" -> 1840
73
100
  - `-h, --help`: show help
74
101
 
75
102
  Examples:
@@ -96,6 +123,8 @@ A full Example:
96
123
  {
97
124
  // Preferred digit count for port (4 or 5)
98
125
  "preferDigitCount": 5,
126
+ // Pad short ports with zero (default: true)
127
+ "paddingZero": true,
99
128
  // Custom letter-to-digit mapping
100
129
  "blockedPorts": [3000, 3001, 3002, 6666],
101
130
  // Port range limits (inclusive)
package/locales/cn.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "optMap": " -m, --map <对象> 自定义映射(JSON 或 JS 对象字面量)",
8
8
  "optLang": " --lang <代码> 输出语言(en 或 cn)",
9
9
  "optDigits": " -d, --digits <位数> 端口优先位数(4 或 5,默认:4)",
10
+ "optPadding": " --padding-zero <true|false> 是否为短端口号补零(默认:true)",
10
11
  "optHelp": " -h, --help 显示帮助",
11
12
  "examples": "示例:",
12
13
  "ex1": " npx @lionad/port-key cfetch # -> 34353",
package/locales/en.json CHANGED
@@ -7,6 +7,7 @@
7
7
  "optMap": " -m, --map <object> Custom mapping (JSON or JS-like object literal)",
8
8
  "optLang": " --lang <code> Output language (en or cn)",
9
9
  "optDigits": " -d, --digits <count> Preferred digit count for port (4 or 5, default: 4)",
10
+ "optPadding": " --padding-zero <true|false> Pad short ports with zero (default: true)",
10
11
  "optHelp": " -h, --help Show help",
11
12
  "examples": "Examples:",
12
13
  "ex1": " npx @lionad/port-key cfetch # -> 34353",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lionad/port-key",
3
- "version": "0.1.6",
3
+ "version": "0.3.0",
4
4
  "description": "A simple, practical port naming strategy",
5
5
  "type": "module",
6
6
  "types": "./src/port-key.d.ts",
@@ -30,10 +30,6 @@
30
30
  "README.md",
31
31
  "LICENSE"
32
32
  ],
33
- "scripts": {
34
- "test": "vitest run",
35
- "test:watch": "vitest"
36
- },
37
33
  "author": "Lionad",
38
34
  "license": "ISC",
39
35
  "repository": {
@@ -53,5 +49,10 @@
53
49
  "dependencies": {},
54
50
  "publishConfig": {
55
51
  "registry": "https://registry.npmjs.org/"
52
+ },
53
+ "scripts": {
54
+ "build": "echo 'skip build steps in packages/core'",
55
+ "test": "vitest run",
56
+ "test:watch": "vitest"
56
57
  }
57
- }
58
+ }
package/src/cli.js CHANGED
@@ -18,6 +18,7 @@ function formatHelp(lang = 'cn') {
18
18
  t.optMap,
19
19
  t.optLang,
20
20
  t.optDigits,
21
+ t.optPadding,
21
22
  t.optHelp,
22
23
  '',
23
24
  t.examples,
@@ -35,6 +36,7 @@ function parseArgv(argv) {
35
36
  let showHelp = false;
36
37
  let lang;
37
38
  let preferDigitCount;
39
+ let paddingZero;
38
40
  const positionals = [];
39
41
 
40
42
  let i = 0;
@@ -78,6 +80,25 @@ function parseArgv(argv) {
78
80
  continue;
79
81
  }
80
82
 
83
+ if (token === '--padding-zero') {
84
+ const value = args[i + 1];
85
+ if (value === 'false') {
86
+ paddingZero = false;
87
+ i += 2;
88
+ } else if (value === 'true') {
89
+ paddingZero = true;
90
+ i += 2;
91
+ } else if (!value || value.startsWith('-')) {
92
+ // Treated as boolean flag (true) if no value provided
93
+ paddingZero = true;
94
+ i += 1;
95
+ } else {
96
+ // Fallback for unexpected values
97
+ throw new Error('Invalid value for --padding-zero. Use true or false.');
98
+ }
99
+ continue;
100
+ }
101
+
81
102
  positionals.push(token);
82
103
  i += 1;
83
104
  }
@@ -87,6 +108,7 @@ function parseArgv(argv) {
87
108
  lang,
88
109
  showHelp,
89
110
  preferDigitCount,
111
+ paddingZero,
90
112
  input: positionals.join(' '),
91
113
  };
92
114
  }
@@ -125,6 +147,7 @@ function runCli(argv, stdout = process.stdout, stderr = process.stderr, deps = {
125
147
  map: parsed.map !== DEFAULT_MAP ? parsed.map : undefined,
126
148
  lang: parsed.lang,
127
149
  preferDigitCount: parsed.preferDigitCount,
150
+ paddingZero: parsed.paddingZero,
128
151
  });
129
152
 
130
153
  const lang = getLangOrDefault(effective.lang);
@@ -151,6 +174,7 @@ function runCli(argv, stdout = process.stdout, stderr = process.stderr, deps = {
151
174
  maxPort: effective.maxPort,
152
175
  preferredRanges: effective.preferredRanges,
153
176
  preferDigitCount: effective.preferDigitCount,
177
+ paddingZero: effective.paddingZero,
154
178
  });
155
179
 
156
180
  if (result.port === null) {
package/src/config.js CHANGED
@@ -54,6 +54,7 @@ function mergeConfig(base, override) {
54
54
  maxPort: b.maxPort ?? a.maxPort,
55
55
  preferredRanges: b.preferredRanges ?? a.preferredRanges,
56
56
  preferDigitCount: b.preferDigitCount ?? a.preferDigitCount,
57
+ paddingZero: b.paddingZero ?? a.paddingZero,
57
58
  lang: b.lang ?? a.lang,
58
59
  };
59
60
  }
package/src/port-key.js CHANGED
@@ -14,30 +14,8 @@ const DEFAULT_MAP = Object.freeze({
14
14
  });
15
15
 
16
16
  const DEFAULT_BLOCKED_PORTS = Object.freeze(
17
+ // well-known application ports
17
18
  new Set([
18
- 0,
19
- 20,
20
- 21,
21
- 22,
22
- 23,
23
- 25,
24
- 53,
25
- 67,
26
- 68,
27
- 80,
28
- 110,
29
- 123,
30
- 143,
31
- 161,
32
- 162,
33
- 389,
34
- 443,
35
- 445,
36
- 465,
37
- 587,
38
- 636,
39
- 993,
40
- 995,
41
19
  3000,
42
20
  3001,
43
21
  5000,
@@ -105,6 +83,8 @@ function isValidPort(port) {
105
83
  }
106
84
 
107
85
  function isPortBlocked(port, blockedPorts) {
86
+ // System Ports (0-1023) are assigned by IETF and should not be used.
87
+ if (port < 1024) return true;
108
88
  if (blockedPorts && typeof blockedPorts.has === 'function') {
109
89
  return blockedPorts.has(port);
110
90
  }
@@ -117,19 +97,45 @@ function isPortBlocked(port, blockedPorts) {
117
97
  function pickPortFromDigits(digits, options = {}) {
118
98
  const raw = String(digits || '').replace(/[^0-9]/g, '');
119
99
  if (!raw) return { port: null, reason: 'No digits found in input' };
120
- if (raw.length < 2) return { port: null, reason: 'Not enough digits to form a candidate' };
100
+
101
+ const paddingZero = options.paddingZero !== false; // Default true
102
+
103
+ // If not padding, enforce strict length check
104
+ // If padding, allow short length as it will be padded
105
+ if (!paddingZero && raw.length < 2) return { port: null, reason: 'Not enough digits to form a candidate' };
121
106
 
122
107
  const minPort = Number.isFinite(options.minPort) ? options.minPort : 0;
123
108
  const maxPort = Number.isFinite(options.maxPort) ? options.maxPort : 65535;
124
109
  const blockedPorts = options.blockedPorts || DEFAULT_BLOCKED_PORTS;
125
110
  const preferDigitCount = options.preferDigitCount || 4;
111
+ // paddingZero is already defined above
126
112
 
127
113
  const candidates = [];
128
114
  const normalized = raw.replace(/^0+/, '');
115
+
116
+ // Padding Logic for short inputs like "air" (184)
117
+ // If normalized length is small, we can pad it with zeros to match preferDigitCount or more
118
+ const paddedCandidates = [];
119
+ if (paddingZero && normalized.length > 0 && normalized.length < preferDigitCount) {
120
+ let current = normalized;
121
+ // Fix: start padding loop immediately
122
+ while (current.length <= 5) {
123
+ // Only push if length >= 2 (valid port min length logic) and >= preferDigitCount (if we want to respect preference)
124
+ // Actually, the original requirement says "pad ... to match preferDigitCount or more".
125
+ if (current.length >= preferDigitCount) {
126
+ paddedCandidates.push(current);
127
+ }
128
+ current += '0';
129
+ }
130
+ }
131
+
129
132
  if (preferDigitCount && normalized.length >= preferDigitCount) {
130
133
  candidates.push(normalized.slice(0, preferDigitCount));
131
134
  candidates.push(normalized.slice(normalized.length - preferDigitCount));
132
135
  } else {
136
+ // If not padding, try smaller lengths (if >= 2)
137
+ // Note: if paddedCandidates is empty, we must rely on this.
138
+ // But if we have paddedCandidates, we can still add these as fallbacks.
133
139
  for (let len = Math.min(normalized.length, preferDigitCount); len >= 2; len -= 1) {
134
140
  candidates.push(normalized.slice(0, len));
135
141
  }
@@ -137,6 +143,9 @@ function pickPortFromDigits(digits, options = {}) {
137
143
  candidates.push(normalized.slice(normalized.length - len));
138
144
  }
139
145
  }
146
+
147
+ // Merge padded candidates
148
+ candidates.push(...paddedCandidates);
140
149
 
141
150
  const unique = Array.from(new Set(candidates));
142
151
  const rejectedCandidates = [];
@@ -219,6 +228,7 @@ export {
219
228
  normalizeInput,
220
229
  mapToDigits,
221
230
  isValidPort,
231
+ isPortBlocked,
222
232
  pickPortFromDigits,
223
233
  mapToPort,
224
234
  parseUserMap,