com.jimuwd.xian.registry-proxy 1.1.20-beta01 → 1.1.23

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.
@@ -5,6 +5,7 @@ import path from 'node:path';
5
5
  import { execa } from 'execa';
6
6
  import findProjectRoot from "../utils/findProjectRoot.js";
7
7
  import { isPortConnectable } from "../utils/portTester.js";
8
+ import { readYarnConfig } from "../utils/configFileReader.js";
8
9
  // Constants
9
10
  const REGISTRY_PROXY_VERSION = process.env.REGISTRY_PROXY_VERSION || 'latest';
10
11
  const LOCK_FILE_NAME = '.registry-proxy-install.lock';
@@ -36,7 +37,7 @@ async function readPortFile(filePath) {
36
37
  const content = await fs.readFile(filePath, 'utf-8');
37
38
  const port = parseInt(content.trim(), 10);
38
39
  if (isNaN(port) || port < 1 || port > 65535) {
39
- throw new Error(`Invalid port number in ${filePath}`);
40
+ throw new Error(`Invalid port number ${port} in ${filePath}`);
40
41
  }
41
42
  return port;
42
43
  }
@@ -51,6 +52,7 @@ async function cleanup(exitCode = 1) {
51
52
  console.error('Cleanup handler error:', err);
52
53
  }
53
54
  }
55
+ console.log("Process exited with exitCode", exitCode);
54
56
  process.exit(exitCode);
55
57
  }
56
58
  function registerCleanup(handler) {
@@ -81,20 +83,22 @@ async function main() {
81
83
  try {
82
84
  await fs.unlink(LOCK_FILE);
83
85
  }
84
- catch {
86
+ catch (err) { //cleanup程序不要抛出任何异常
87
+ console.error(`Failed to delete lock file: ${LOCK_FILE}`, err);
85
88
  }
86
89
  });
87
90
  // Change to project root
88
91
  process.chdir(INSTALLATION_ROOT);
89
92
  // Start registry proxy
90
93
  console.log(`Starting registry-proxy@${REGISTRY_PROXY_VERSION} in the background...`);
94
+ // 提示:这里借助了execa调用"yarn dlx"后台运行registry proxy server的功能,没有直接使用本地ts函数调用的方式启动本地代理服务器,因为后者不太容易达到后台运行的效果。
91
95
  proxyProcess = execa('yarn', [
92
96
  'dlx', '-p', `com.jimuwd.xian.registry-proxy@${REGISTRY_PROXY_VERSION}`,
93
97
  'registry-proxy',
94
98
  '.registry-proxy.yml',
95
99
  '.yarnrc.yml',
96
100
  path.join(process.env.HOME || '', '.yarnrc.yml'),
97
- '40061'
101
+ /*之前是写死的静态端口40061,它有个缺点就是本地无法为多个项目工程并发执行yarn-install,现改为使用随机可用端口作为本地代理服务器端口,传'0'/''空串即可*/ '0'
98
102
  ], {
99
103
  detached: true,
100
104
  stdio: 'inherit'
@@ -105,11 +109,11 @@ async function main() {
105
109
  try {
106
110
  proxyProcess.kill('SIGTERM');
107
111
  await proxyProcess;
112
+ console.log('Proxy server stopped.');
108
113
  }
109
- catch {
110
- // Ignore errors
114
+ catch (err) { // cleanup程序不要抛出异常
115
+ console.error('Proxy server stopping with error', err);
111
116
  }
112
- console.log('Proxy server stopped.');
113
117
  }
114
118
  });
115
119
  // Wait for proxy to start
@@ -124,14 +128,32 @@ async function main() {
124
128
  throw new Error(`Proxy server not listening on port ${PROXY_PORT}`);
125
129
  }
126
130
  // Configure yarn
131
+ const { exitCode, stdout } = await execa('yarn', ['config', 'get', 'npmRegistryServer']);
132
+ let npmRegistryServer = stdout.trim();
133
+ let localNpmRegistryServer;
134
+ try {
135
+ const localYarnConfig = await readYarnConfig('.yarnrc.yml');
136
+ localNpmRegistryServer = localYarnConfig.npmRegistryServer;
137
+ }
138
+ catch {
139
+ localNpmRegistryServer = undefined;
140
+ }
127
141
  await execa('yarn', ['config', 'set', 'npmRegistryServer', `http://127.0.0.1:${PROXY_PORT}`]);
128
142
  console.log(`Set npmRegistryServer to http://127.0.0.1:${PROXY_PORT}`);
129
143
  registerCleanup(async () => {
130
144
  try {
131
- await execa('yarn', ['config', 'unset', 'npmRegistryServer']); //fixme:这里使用unset,有个缺点就是如果工程中.yarnrc.yml原本配置的只读registryServer没有恢复而是被清空了!国内的网络再执行任何拉取操作或yarn dlx命令容易超时!
132
- console.log('Cleared npmRegistryServer configuration:这里清空npmRegistryServer而不是恢复,有个缺点就是如果工程中.yarnrc.yml原本配置的只读registryServer没有恢复而是被清空了!国内的网络再执行任何拉取操作或yarn dlx命令容易超时!');
145
+ //if (exitCode === 0 && npmRegistryServer) {//不能用这个变量来恢复为原来的 npmRegistryServer,因为它可能是全局配置~/.yarnrc.yml内的配置值或yarn工具官方默认值,而非本地.yarnrc.yml配置值。
146
+ if (localNpmRegistryServer && localNpmRegistryServer.trim()) { //恢复为本地配置文件原来的 npmRegistryServer 配置值
147
+ await execa('yarn', ['config', 'set', 'npmRegistryServer', localNpmRegistryServer]);
148
+ console.log(`Recover npmRegistryServer to ${localNpmRegistryServer}`);
149
+ }
150
+ else { //原来本地配置文件中没有npmRegistryServer,则重置npmRegistryServer
151
+ await execa('yarn', ['config', 'unset', 'npmRegistryServer']);
152
+ console.log(`Unset npmRegistryServer.`);
153
+ }
133
154
  }
134
- catch {
155
+ catch (err) { //cleanup程序不要抛出异常
156
+ console.error('Recover yarn config npmRegistryServer error.', err);
135
157
  }
136
158
  });
137
159
  // Run yarn install
@@ -143,6 +165,7 @@ async function main() {
143
165
  throw new Error('yarn install failed');
144
166
  }
145
167
  // Success
168
+ console.info("Yarn install with local registry-proxy server success.");
146
169
  await cleanup(0);
147
170
  }
148
171
  catch (err) {
@@ -168,8 +191,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
168
191
  });
169
192
  });
170
193
  // Start the program
171
- main().catch(err => {
172
- console.error('Unhandled error:', err);
173
- cleanup(1);
174
- });
194
+ await main();
175
195
  }
@@ -0,0 +1,34 @@
1
+ interface RegistryConfig {
2
+ npmAuthToken?: string;
3
+ }
4
+ interface HttpsConfig {
5
+ key: string;
6
+ cert: string;
7
+ }
8
+ interface ProxyConfig {
9
+ registries: Record<string, RegistryConfig | null>;
10
+ https?: HttpsConfig;
11
+ basePath?: string;
12
+ }
13
+ interface YarnConfig {
14
+ npmRegistryServer?: string | null | undefined;
15
+ npmRegistries?: Record<string, RegistryConfig | null>;
16
+ }
17
+ interface RegistryInfo {
18
+ normalizedRegistryUrl: string;
19
+ token?: string;
20
+ }
21
+ interface ProxyInfo {
22
+ registries: RegistryInfo[];
23
+ https?: HttpsConfig;
24
+ basePath?: string;
25
+ }
26
+ interface PackageVersion {
27
+ dist?: {
28
+ tarball?: string;
29
+ };
30
+ }
31
+ interface PackageData {
32
+ versions?: Record<string, PackageVersion>;
33
+ }
34
+ export { RegistryConfig, HttpsConfig, ProxyConfig, YarnConfig, RegistryInfo, ProxyInfo, PackageVersion, PackageData };
package/dist/models.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import http, { createServer } from 'node:http';
3
3
  import https, { createServer as createHttpsServer } from 'node:https';
4
- import { existsSync, promises as fsPromises, readFileSync } from 'node:fs';
5
- import { load } from 'js-yaml';
4
+ import { promises as fsPromises, readFileSync } from 'node:fs';
6
5
  import fetch from 'node-fetch';
7
6
  import { homedir } from 'os';
8
- import { join, resolve } from 'path';
7
+ import { join } from 'path';
9
8
  import { URL } from 'url';
10
9
  import logger from "../utils/logger.js";
11
10
  import ConcurrencyLimiter from "../utils/ConcurrencyLimiter.js";
12
11
  import { gracefulShutdown, registerProcessShutdownHook } from "./gracefullShutdown.js";
13
12
  import { writePortFile } from "../port.js";
14
13
  import resolveEnvValue from "../utils/resolveEnvValue.js";
15
- const { readFile } = fsPromises;
14
+ import { readYarnConfig, resolvePath, } from "../utils/configFileReader.js";
15
+ import { readProxyConfig, } from "./serverConfigReader.js";
16
16
  // 整个registry-proxy server实例 使用的全局限流器
17
17
  const LIMITER = new ConcurrencyLimiter(5);
18
18
  function removeEndingSlashAndForceStartingSlash(str) {
@@ -44,9 +44,6 @@ function normalizeUrl(httpOrHttpsUrl) {
44
44
  throw new Error(`Invalid URL: ${httpOrHttpsUrl}`, { cause: e });
45
45
  }
46
46
  }
47
- function resolvePath(path) {
48
- return path.startsWith('~/') ? join(homedir(), path.slice(2)) : resolve(path);
49
- }
50
47
  function removeRegistryPrefix(tarballUrl, registries) {
51
48
  const normalizedTarball = normalizeUrl(tarballUrl);
52
49
  const normalizedRegistries = registries
@@ -59,48 +56,6 @@ function removeRegistryPrefix(tarballUrl, registries) {
59
56
  }
60
57
  throw new Error(`Can't find tarball url ${tarballUrl} does not match given registries ${normalizedRegistries}`);
61
58
  }
62
- /**
63
- * 读取yml配置文件得到配置值对象{@link ProxyConfig}
64
- * @note 本读取操作不会解析环境变量值
65
- * @param proxyConfigPath 配置文件路径
66
- */
67
- async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml') {
68
- let config = undefined;
69
- const resolvedPath = resolvePath(proxyConfigPath);
70
- try {
71
- const content = await readFile(resolvedPath, 'utf8');
72
- config = load(content);
73
- }
74
- catch (e) {
75
- logger.error(`Failed to load proxy config from ${resolvedPath}:`, e);
76
- await gracefulShutdown();
77
- }
78
- if (!config?.registries) {
79
- logger.error('Missing required "registries" field in config');
80
- await gracefulShutdown();
81
- }
82
- return config;
83
- }
84
- /**
85
- * 读取yml配置文件为yml对象
86
- * @param path yml文件路径
87
- */
88
- async function readYarnConfig(path) {
89
- try {
90
- if (existsSync(path)) {
91
- const content = await readFile(resolvePath(path), 'utf8');
92
- return load(content);
93
- }
94
- else {
95
- logger.info(`Skip reading ${path}, because it does not exist.`);
96
- return {};
97
- }
98
- }
99
- catch (e) {
100
- logger.warn(`Failed to load Yarn config from ${path}:`, e);
101
- return {};
102
- }
103
- }
104
59
  async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYarnConfigPath = './.yarnrc.yml', globalYarnConfigPath = join(homedir(), '.yarnrc.yml')) {
105
60
  const [proxyConfig, localYarnConfig, globalYarnConfig] = await Promise.all([
106
61
  readProxyConfig(proxyConfigPath),
@@ -0,0 +1,8 @@
1
+ import { ProxyConfig } from "../models.js";
2
+ /**
3
+ * 读取yml配置文件得到配置值对象{@link ProxyConfig}
4
+ * @note 本读取操作不会解析环境变量值
5
+ * @param proxyConfigPath 配置文件路径
6
+ */
7
+ declare function readProxyConfig(proxyConfigPath?: string): Promise<ProxyConfig>;
8
+ export { readProxyConfig };
@@ -0,0 +1,29 @@
1
+ import { load } from "js-yaml";
2
+ import { promises as fsPromises } from 'node:fs';
3
+ import { gracefulShutdown } from "./gracefullShutdown.js";
4
+ import { resolvePath } from "../utils/configFileReader.js";
5
+ import logger from "../utils/logger.js";
6
+ const { readFile } = fsPromises;
7
+ /**
8
+ * 读取yml配置文件得到配置值对象{@link ProxyConfig}
9
+ * @note 本读取操作不会解析环境变量值
10
+ * @param proxyConfigPath 配置文件路径
11
+ */
12
+ async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml') {
13
+ let config = undefined;
14
+ const resolvedPath = resolvePath(proxyConfigPath);
15
+ try {
16
+ const content = await readFile(resolvedPath, 'utf8');
17
+ config = load(content);
18
+ }
19
+ catch (e) {
20
+ logger.error(`Failed to load proxy config from ${resolvedPath}:`, e);
21
+ await gracefulShutdown();
22
+ }
23
+ if (!config?.registries) {
24
+ logger.error('Missing required "registries" field in config');
25
+ await gracefulShutdown();
26
+ }
27
+ return config;
28
+ }
29
+ export { readProxyConfig };
@@ -0,0 +1,12 @@
1
+ import { YarnConfig } from "../models.js";
2
+ /**
3
+ * 读取yml配置文件为yml对象
4
+ * @param path yml文件路径
5
+ */
6
+ declare function readYarnConfig(path: string): Promise<YarnConfig>;
7
+ /**
8
+ * 标准化处理路径值
9
+ * @param path
10
+ */
11
+ declare function resolvePath(path: string): string;
12
+ export { readYarnConfig, resolvePath, };
@@ -0,0 +1,34 @@
1
+ import { load } from "js-yaml";
2
+ import { join, resolve } from "path";
3
+ import { homedir } from "os";
4
+ import { existsSync, promises as fsPromises } from 'node:fs';
5
+ import logger from "./logger.js";
6
+ const { readFile } = fsPromises;
7
+ /**
8
+ * 读取yml配置文件为yml对象
9
+ * @param path yml文件路径
10
+ */
11
+ async function readYarnConfig(path) {
12
+ try {
13
+ if (existsSync(path)) {
14
+ const content = await readFile(resolvePath(path), 'utf8');
15
+ return load(content);
16
+ }
17
+ else {
18
+ logger.info(`Skip reading ${path}, because it does not exist.`);
19
+ return {};
20
+ }
21
+ }
22
+ catch (e) {
23
+ logger.warn(`Failed to load Yarn config from ${path}:`, e);
24
+ return {};
25
+ }
26
+ }
27
+ /**
28
+ * 标准化处理路径值
29
+ * @param path
30
+ */
31
+ function resolvePath(path) {
32
+ return path.startsWith('~/') ? join(homedir(), path.slice(2)) : resolve(path);
33
+ }
34
+ export { readYarnConfig, resolvePath, };
@@ -15,10 +15,10 @@ const PREFIX = {
15
15
  };
16
16
  // 代理服务器专用日志
17
17
  const logger = {
18
- info: (...args) => console.log(`${PREFIX.proxy}`, ...args),
19
- success: (...args) => console.log(`${PREFIX.proxy} ${COLORS.success}✓${COLORS.reset}`, ...args),
18
+ info: (...args) => { },
19
+ success: (...args) => { },
20
20
  error: (...args) => console.error(`${PREFIX.error}`, ...args),
21
- warn: (...args) => console.warn(`${PREFIX.warn}`, ...args),
21
+ warn: (...args) => { },
22
22
  debug: (...args) => process.env.DEBUG && console.debug(`${PREFIX.debug}`, ...args)
23
23
  };
24
24
  export default logger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.1.20-beta01",
3
+ "version": "1.1.23",
4
4
  "description": "A lightweight npm registry local proxy with fallback support",
5
5
  "type": "module",
6
6
  "main": "dist/server/index.js",