com.jimuwd.xian.registry-proxy 1.0.135 → 1.1.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.
@@ -11,6 +11,7 @@ import logger from "../utils/logger.js";
11
11
  import ConcurrencyLimiter from "../utils/ConcurrencyLimiter.js";
12
12
  import { gracefulShutdown, registerProcessShutdownHook } from "./gracefullShutdown.js";
13
13
  import { writePortFile } from "../port.js";
14
+ import resolveEnvValue from "../utils/resolveEnvValue.js";
14
15
  const { readFile } = fsPromises;
15
16
  const limiter = new ConcurrencyLimiter(Infinity);
16
17
  function removeEndingSlashAndForceStartingSlash(str) {
@@ -57,6 +58,11 @@ function removeRegistryPrefix(tarballUrl, registries) {
57
58
  }
58
59
  throw new Error(`Can't find tarball url ${tarballUrl} does not match given registries ${normalizedRegistries}`);
59
60
  }
61
+ /**
62
+ * 读取yml配置文件得到配置值对象{@link ProxyConfig}
63
+ * @note 本读取操作不会解析环境变量值
64
+ * @param proxyConfigPath 配置文件路径
65
+ */
60
66
  async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml') {
61
67
  let config = undefined;
62
68
  const resolvedPath = resolvePath(proxyConfigPath);
@@ -74,6 +80,10 @@ async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml') {
74
80
  }
75
81
  return config;
76
82
  }
83
+ /**
84
+ * 读取yml配置文件为yml对象
85
+ * @param path yml文件路径
86
+ */
77
87
  async function readYarnConfig(path) {
78
88
  try {
79
89
  const content = await readFile(resolvePath(path), 'utf8');
@@ -93,7 +103,7 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
93
103
  const registryMap = new Map();
94
104
  for (const [proxiedRegUrl, proxyRegConfig] of Object.entries(proxyConfig.registries)) {
95
105
  const normalizedProxiedRegUrl = normalizeUrl(proxiedRegUrl);
96
- let token = proxyRegConfig?.npmAuthToken;
106
+ let token = resolveEnvValue(proxyRegConfig?.npmAuthToken);
97
107
  if (!token) {
98
108
  const yarnConfigs = [localYarnConfig, globalYarnConfig];
99
109
  for (const yarnConfig of yarnConfigs) {
@@ -103,6 +113,7 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
103
113
  if (foundEntry) {
104
114
  const [, registryConfig] = foundEntry;
105
115
  if (registryConfig?.npmAuthToken) {
116
+ // .yarnrc.yml内的配置值,暂时不处理环境变量值,未来按需扩展
106
117
  token = registryConfig.npmAuthToken;
107
118
  break;
108
119
  }
@@ -110,7 +121,10 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
110
121
  }
111
122
  }
112
123
  }
113
- registryMap.set(normalizedProxiedRegUrl, { normalizedRegistryUrl: normalizedProxiedRegUrl, token });
124
+ registryMap.set(normalizedProxiedRegUrl, {
125
+ normalizedRegistryUrl: normalizedProxiedRegUrl,
126
+ token: token ? token : undefined
127
+ });
114
128
  }
115
129
  const registries = Array.from(registryMap.values());
116
130
  const https = proxyConfig.https;
@@ -249,7 +263,7 @@ async function writeResponseToDownstreamClient(registryInfo, targetUrl, resToDow
249
263
  // pipe upstream body-stream to downstream stream and automatically ends the stream to downstream when upstream stream is ended.
250
264
  upstreamResponse.body.pipe(resToDownstreamClient, { end: true });
251
265
  upstreamResponse.body
252
- .on('data', (chunk) => logger.info(() => `Chunk transferred from ${targetUrl} to downstream client size=${Buffer.byteLength(chunk)}bytes`))
266
+ .on('data', (chunk) => logger.info(`Chunk transferred from ${targetUrl} to downstream client size=${Buffer.byteLength(chunk)}bytes`))
253
267
  .on('end', () => logger.info(`Upstream server ${targetUrl} response.body stream ended.`))
254
268
  // connection will be closed automatically when all chunk data is transferred (after stream ends).
255
269
  .on('close', () => logger.debug(() => `Upstream ${targetUrl} closed stream.`))
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 解析字符串中的环境变量占位符(格式为 `${ENV_VAR}`),并替换为实际环境变量的值。
3
+ * - 若环境变量不存在,则占位符会被替换为空字符串 `''`。
4
+ * - 若输入为 `null` 或 `undefined`,直接返回原值(不处理)。
5
+ *
6
+ * @param str - 待处理的字符串,可能包含环境变量占位符(如 `${API_URL}`)。支持 `null` 或 `undefined`。
7
+ * @returns 处理后的字符串。若输入为 `null` 或 `undefined`,返回原值;否则返回替换后的新字符串。
8
+ *
9
+ * @example
10
+ * // 环境变量未定义时,替换为 ''
11
+ * resolveEnvValue('${UNDEFINED_VAR}'); // => ''
12
+ *
13
+ * // 混合替换
14
+ * resolveEnvValue('Host: ${HOST}, Port: ${PORT}'); // => 'Host: 127.0.0.1, Port: 3000'(假设环境变量已定义)
15
+ *
16
+ * // 保留 null/undefined
17
+ * resolveEnvValue(null); // => null
18
+ */
19
+ export default function resolveEnvValue(str: string | null | undefined): string | null | undefined;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * 解析字符串中的环境变量占位符(格式为 `${ENV_VAR}`),并替换为实际环境变量的值。
3
+ * - 若环境变量不存在,则占位符会被替换为空字符串 `''`。
4
+ * - 若输入为 `null` 或 `undefined`,直接返回原值(不处理)。
5
+ *
6
+ * @param str - 待处理的字符串,可能包含环境变量占位符(如 `${API_URL}`)。支持 `null` 或 `undefined`。
7
+ * @returns 处理后的字符串。若输入为 `null` 或 `undefined`,返回原值;否则返回替换后的新字符串。
8
+ *
9
+ * @example
10
+ * // 环境变量未定义时,替换为 ''
11
+ * resolveEnvValue('${UNDEFINED_VAR}'); // => ''
12
+ *
13
+ * // 混合替换
14
+ * resolveEnvValue('Host: ${HOST}, Port: ${PORT}'); // => 'Host: 127.0.0.1, Port: 3000'(假设环境变量已定义)
15
+ *
16
+ * // 保留 null/undefined
17
+ * resolveEnvValue(null); // => null
18
+ */
19
+ export default function resolveEnvValue(str) {
20
+ // 1. 处理 null 或 undefined 输入:直接返回原值
21
+ if (str == null) {
22
+ return str;
23
+ }
24
+ // 2. 使用正则表达式全局匹配所有 ${...} 占位符
25
+ // - 正则说明: \${(.+?)}
26
+ // - \${ 匹配字面量 `${`
27
+ // - (.+?) 非贪婪匹配任意字符(环境变量名),直到遇到第一个 `}`
28
+ // - /g 标志确保替换全部匹配项(而非仅第一个)
29
+ // - 替换逻辑: 若 process.env[key] 不存在(undefined/null),则返回 ''
30
+ return str.replace(/\${(.+?)}/g, (_, key) => {
31
+ return process.env[key] ?? '';
32
+ });
33
+ }
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.135",
3
+ "version": "1.1.1",
4
4
  "description": "A lightweight npm registry proxy with fallback support",
5
5
  "type": "module",
6
6
  "main": "dist/server/index.js",
7
7
  "bin": {
8
8
  "registry-proxy": "dist/server/index.js",
9
- "yarn-install": "dist/client/yarn-install.js",
10
- "yarn-install-bash": "src/client/yarn-install.sh"
9
+ "yarn-install": "dist/client/yarn-install.js"
11
10
  },
12
11
  "files": [
13
12
  "dist",
@@ -1,5 +1,8 @@
1
1
  #!/bin/bash
2
2
 
3
+ eco "@@Deprecated@@"
4
+ eco "use yarn-install.ts instead!"
5
+
3
6
  # 启用严格模式,但移除 set -e,手动处理错误
4
7
  set -u # 未定义变量时退出
5
8
  set -o pipefail # 管道中任一命令失败时退出