com.jimuwd.xian.registry-proxy 1.0.36 → 1.0.37

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.
Files changed (3) hide show
  1. package/dist/index.js +51 -46
  2. package/package.json +1 -1
  3. package/src/index.ts +59 -59
package/dist/index.js CHANGED
@@ -139,10 +139,9 @@ async function loadProxyInfo(proxyConfigPath = './.registry-proxy.yml', localYar
139
139
  const basePath = removeEndingSlashAndForceStartingSlash(proxyConfig.basePath);
140
140
  return { registries, https, basePath };
141
141
  }
142
- async function fetchFromRegistry(registry, path, search, limiter) {
142
+ async function fetchFromRegistry(registry, targetUrl, limiter) {
143
143
  await limiter.acquire();
144
144
  try {
145
- const targetUrl = `${registry.normalizedRegistryUrl}${path}${search || ''}`;
146
145
  console.log(`Fetching from: ${targetUrl}`);
147
146
  const headers = registry.token ? { Authorization: `Bearer ${registry.token}` } : undefined;
148
147
  const response = await fetch(targetUrl, { headers });
@@ -161,25 +160,28 @@ async function fetchFromRegistry(registry, path, search, limiter) {
161
160
  limiter.release();
162
161
  }
163
162
  }
164
- // 修改后的 writeResponse 函数
165
- async function writeResponse(res, response, req, proxyInfo, proxyPort, registryInfos) {
166
- const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
167
- // 准备通用头信息
168
- const safeHeaders = {
169
- 'content-type': contentType,
170
- 'connection': 'keep-alive'
171
- };
172
- // 复制所有可能需要的头信息
173
- const headersToCopy = [
174
- 'cache-control', 'etag', 'last-modified',
175
- 'content-encoding', 'content-length'
176
- ];
177
- headersToCopy.forEach(header => {
178
- const value = response.headers.get(header);
179
- if (value)
180
- safeHeaders[header] = value;
181
- });
163
+ // 修改后的 writeSuccessfulResponse 函数
164
+ async function writeSuccessfulResponse(registryInfo, targetUrl, res, response, req, proxyInfo, proxyPort, registryInfos) {
165
+ if (!response.ok)
166
+ throw new Error("Only 2xx response is supported");
182
167
  try {
168
+ const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
169
+ // 准备通用头信息
170
+ const safeHeaders = {
171
+ 'content-type': contentType,
172
+ 'connection': 'keep-alive'
173
+ };
174
+ // 复制所有可能需要的头信息
175
+ const headersToCopy = [
176
+ 'cache-control', 'etag', 'last-modified',
177
+ 'content-encoding', 'content-length'
178
+ ];
179
+ headersToCopy.forEach(header => {
180
+ const value = response.headers.get(header);
181
+ if (value)
182
+ safeHeaders[header] = value;
183
+ });
184
+ res.writeHead(response.status, safeHeaders);
183
185
  if (contentType.includes('application/json')) {
184
186
  // JSON 处理逻辑
185
187
  const data = await response.json();
@@ -194,38 +196,36 @@ async function writeResponse(res, response, req, proxyInfo, proxyPort, registryI
194
196
  }
195
197
  }
196
198
  }
197
- res.writeHead(200, safeHeaders);
198
199
  res.end(JSON.stringify(data));
199
200
  }
200
201
  else {
201
202
  // 二进制流处理
202
203
  if (!response.body) {
203
- console.error('Empty response body');
204
- process.exit(1);
205
- }
206
- // 修复流类型转换问题
207
- let nodeStream;
208
- if (typeof Readable.fromWeb === 'function') {
209
- // Node.js 17+ 标准方式
210
- nodeStream = Readable.fromWeb(response.body);
204
+ console.error(`Empty response body from ${targetUrl}`);
211
205
  }
212
206
  else {
213
- // Node.js 16 及以下版本的兼容方案
214
- const { PassThrough } = await import('stream');
215
- const passThrough = new PassThrough();
216
- const reader = response.body.getReader();
217
- const pump = async () => {
218
- const { done, value } = await reader.read();
219
- if (done)
220
- return passThrough.end();
221
- passThrough.write(value);
222
- await pump();
223
- };
224
- pump().catch(err => passThrough.destroy(err));
225
- nodeStream = passThrough;
207
+ let nodeStream;
208
+ if (typeof Readable.fromWeb === 'function') {
209
+ // Node.js 17+ 标准方式
210
+ nodeStream = Readable.fromWeb(response.body);
211
+ }
212
+ else {
213
+ // Node.js 16 及以下版本的兼容方案
214
+ const { PassThrough } = await import('stream');
215
+ const passThrough = new PassThrough();
216
+ const reader = response.body.getReader();
217
+ const pump = async () => {
218
+ const { done, value } = await reader.read();
219
+ if (done)
220
+ return passThrough.end();
221
+ passThrough.write(value);
222
+ await pump();
223
+ };
224
+ pump().catch(err => passThrough.destroy(err));
225
+ nodeStream = passThrough;
226
+ }
227
+ await pipeline(nodeStream, res);
226
228
  }
227
- res.writeHead(response.status, safeHeaders);
228
- await pipeline(nodeStream, res);
229
229
  }
230
230
  }
231
231
  catch (err) {
@@ -258,10 +258,15 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
258
258
  : fullUrl.pathname.slice(basePathPrefixedWithSlash.length);
259
259
  // 顺序尝试注册表,获取第一个成功响应
260
260
  let successfulResponse = null;
261
+ let targetRegistry = null;
262
+ let targetUrl = null;
261
263
  for (const registry of registryInfos) {
262
264
  if (req.destroyed)
263
265
  break;
264
- const response = await fetchFromRegistry(registry, path, fullUrl.search, limiter);
266
+ targetRegistry = registry;
267
+ const search = fullUrl.search || '';
268
+ targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
269
+ const response = await fetchFromRegistry(registry, targetUrl, limiter);
265
270
  if (response) {
266
271
  successfulResponse = response;
267
272
  break;
@@ -269,7 +274,7 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
269
274
  }
270
275
  // 统一回写响应
271
276
  if (successfulResponse) {
272
- await writeResponse(res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
277
+ await writeSuccessfulResponse(targetRegistry, targetUrl, res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
273
278
  }
274
279
  else {
275
280
  res.writeHead(404).end('All upstream registries failed');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.36",
3
+ "version": "1.0.37",
4
4
  "type": "module",
5
5
  "description": "A lightweight npm registry proxy with fallback support",
6
6
  "main": "dist/index.js",
package/src/index.ts CHANGED
@@ -189,16 +189,14 @@ async function loadProxyInfo(
189
189
 
190
190
  async function fetchFromRegistry(
191
191
  registry: RegistryInfo,
192
- path: string,
193
- search: string,
192
+ targetUrl: string,
194
193
  limiter: ConcurrencyLimiter
195
194
  ): Promise<Response | null> {
196
195
  await limiter.acquire();
197
196
  try {
198
- const targetUrl = `${registry.normalizedRegistryUrl}${path}${search || ''}`;
199
197
  console.log(`Fetching from: ${targetUrl}`);
200
- const headers = registry.token ? { Authorization: `Bearer ${registry.token}` } : undefined;
201
- const response = await fetch(targetUrl, { headers });
198
+ const headers = registry.token ? {Authorization: `Bearer ${registry.token}`} : undefined;
199
+ const response = await fetch(targetUrl, {headers});
202
200
  console.log(`Response from ${targetUrl}: ${response.status} ${response.statusText}`);
203
201
  return response.ok ? response : null;
204
202
  } catch (e) {
@@ -215,8 +213,10 @@ async function fetchFromRegistry(
215
213
  }
216
214
  }
217
215
 
218
- // 修改后的 writeResponse 函数
219
- async function writeResponse(
216
+ // 修改后的 writeSuccessfulResponse 函数
217
+ async function writeSuccessfulResponse(
218
+ registryInfo: RegistryInfo,
219
+ targetUrl: string,
220
220
  res: ServerResponse,
221
221
  response: Response,
222
222
  req: IncomingMessage,
@@ -224,26 +224,32 @@ async function writeResponse(
224
224
  proxyPort: number,
225
225
  registryInfos: RegistryInfo[]
226
226
  ): Promise<void> {
227
- const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
228
227
 
229
- // 准备通用头信息
230
- const safeHeaders: OutgoingHttpHeaders = {
231
- 'content-type': contentType,
232
- 'connection': 'keep-alive'
233
- };
228
+ if (!response.ok) throw new Error("Only 2xx response is supported");
234
229
 
235
- // 复制所有可能需要的头信息
236
- const headersToCopy = [
237
- 'cache-control', 'etag', 'last-modified',
238
- 'content-encoding', 'content-length'
239
- ];
230
+ try {
231
+ const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
240
232
 
241
- headersToCopy.forEach(header => {
242
- const value = response.headers.get(header);
243
- if (value) safeHeaders[header] = value;
244
- });
233
+ // 准备通用头信息
234
+ const safeHeaders: OutgoingHttpHeaders = {
235
+ 'content-type': contentType,
236
+ 'connection': 'keep-alive'
237
+ };
238
+
239
+ // 复制所有可能需要的头信息
240
+ const headersToCopy = [
241
+ 'cache-control', 'etag', 'last-modified',
242
+ 'content-encoding', 'content-length'
243
+ ];
244
+
245
+ headersToCopy.forEach(header => {
246
+ const value = response.headers.get(header);
247
+ if (value) safeHeaders[header] = value;
248
+ });
249
+
250
+
251
+ res.writeHead(response.status, safeHeaders);
245
252
 
246
- try {
247
253
  if (contentType.includes('application/json')) {
248
254
  // JSON 处理逻辑
249
255
  const data = await response.json() as PackageData;
@@ -259,39 +265,35 @@ async function writeResponse(
259
265
  }
260
266
  }
261
267
  }
262
- res.writeHead(200, safeHeaders);
263
268
  res.end(JSON.stringify(data));
264
269
  } else {
265
270
  // 二进制流处理
266
271
  if (!response.body) {
267
- console.error('Empty response body');
268
- process.exit(1);
269
- }
270
-
271
- // 修复流类型转换问题
272
- let nodeStream: NodeJS.ReadableStream;
273
- if (typeof Readable.fromWeb === 'function') {
274
- // Node.js 17+ 标准方式
275
- nodeStream = Readable.fromWeb(response.body as any);
272
+ console.error(`Empty response body from ${targetUrl}`);
276
273
  } else {
277
- // Node.js 16 及以下版本的兼容方案
278
- const { PassThrough } = await import('stream');
279
- const passThrough = new PassThrough();
280
- const reader = (response.body as any).getReader();
281
-
282
- const pump = async () => {
283
- const { done, value } = await reader.read();
284
- if (done) return passThrough.end();
285
- passThrough.write(value);
286
- await pump();
287
- };
288
-
289
- pump().catch(err => passThrough.destroy(err));
290
- nodeStream = passThrough;
291
- }
274
+ let nodeStream: NodeJS.ReadableStream;
275
+ if (typeof Readable.fromWeb === 'function') {
276
+ // Node.js 17+ 标准方式
277
+ nodeStream = Readable.fromWeb(response.body as any);
278
+ } else {
279
+ // Node.js 16 及以下版本的兼容方案
280
+ const {PassThrough} = await import('stream');
281
+ const passThrough = new PassThrough();
282
+ const reader = (response.body as any).getReader();
283
+
284
+ const pump = async () => {
285
+ const {done, value} = await reader.read();
286
+ if (done) return passThrough.end();
287
+ passThrough.write(value);
288
+ await pump();
289
+ };
290
+
291
+ pump().catch(err => passThrough.destroy(err));
292
+ nodeStream = passThrough;
293
+ }
292
294
 
293
- res.writeHead(response.status, safeHeaders);
294
- await pipeline(nodeStream, res);
295
+ await pipeline(nodeStream, res);
296
+ }
295
297
  }
296
298
  } catch (err) {
297
299
  console.error('Failed to write response:', err);
@@ -335,16 +337,14 @@ export async function startProxyServer(
335
337
 
336
338
  // 顺序尝试注册表,获取第一个成功响应
337
339
  let successfulResponse: Response | null = null;
340
+ let targetRegistry: RegistryInfo | null = null;
341
+ let targetUrl: string | null = null;
338
342
  for (const registry of registryInfos) {
339
343
  if (req.destroyed) break;
340
-
341
- const response = await fetchFromRegistry(
342
- registry,
343
- path,
344
- fullUrl.search,
345
- limiter
346
- );
347
-
344
+ targetRegistry = registry;
345
+ const search = fullUrl.search || '';
346
+ targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
347
+ const response = await fetchFromRegistry(registry, targetUrl, limiter);
348
348
  if (response) {
349
349
  successfulResponse = response;
350
350
  break;
@@ -353,7 +353,7 @@ export async function startProxyServer(
353
353
 
354
354
  // 统一回写响应
355
355
  if (successfulResponse) {
356
- await writeResponse(res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
356
+ await writeSuccessfulResponse(targetRegistry!, targetUrl!, res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
357
357
  } else {
358
358
  res.writeHead(404).end('All upstream registries failed');
359
359
  }