com.jimuwd.xian.registry-proxy 1.0.23 → 1.0.25

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/dist/index.js CHANGED
@@ -34,6 +34,13 @@ class ConcurrencyLimiter {
34
34
  }
35
35
  }
36
36
  const limiter = new ConcurrencyLimiter(3);
37
+ function removeEndingSlashButReserveStartingSlash(str) {
38
+ if (!str)
39
+ return '/';
40
+ if (str === '/')
41
+ return '/';
42
+ return str.replace(/\/+$/, '');
43
+ }
37
44
  function normalizeUrl(url) {
38
45
  try {
39
46
  const urlObj = new URL(url);
@@ -43,7 +50,7 @@ function normalizeUrl(url) {
43
50
  else if (urlObj.protocol === 'https:' && (urlObj.port === '443' || urlObj.port === '')) {
44
51
  urlObj.port = '';
45
52
  }
46
- urlObj.pathname = urlObj.pathname.replace(/\/+$/, '');
53
+ urlObj.pathname = removeEndingSlashButReserveStartingSlash(urlObj.pathname);
47
54
  return urlObj.toString();
48
55
  }
49
56
  catch (e) {
@@ -176,8 +183,10 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
176
183
  try {
177
184
  const data = await successResponse.json();
178
185
  if (data.versions) {
179
- console.log("Requested url is", req.url);
180
- const proxyBaseUrlNoSuffixedWithSlash = `${proxyConfig.https ? 'https' : 'http'}://${req.headers.host || 'localhost:' + proxyPort}${basePathPrefixedWithSlash === '/' ? '' : basePathPrefixedWithSlash}`;
186
+ const requestHeadersHostFromYarnClient = req.headers.host || 'localhost:' + proxyPort;
187
+ console.log("Request headers.host from yarn client is", requestHeadersHostFromYarnClient);
188
+ const proxyBaseUrlNoSuffixedWithSlash = `${proxyConfig.https ? 'https' : 'http'}://${requestHeadersHostFromYarnClient}${basePathPrefixedWithSlash === '/' ? '' : basePathPrefixedWithSlash}`;
189
+ console.log("proxyBaseUrlNoSuffixedWithSlash", proxyBaseUrlNoSuffixedWithSlash);
181
190
  for (const version in data.versions) {
182
191
  const dist = data.versions[version]?.dist;
183
192
  if (dist?.tarball) {
@@ -185,6 +194,8 @@ export async function startProxyServer(proxyConfigPath, localYarnConfigPath, glo
185
194
  const originalSearchParamsStr = originalUrl.search || '';
186
195
  const tarballPathPrefixedWithSlash = removeRegistryPrefix(dist.tarball, registries);
187
196
  dist.tarball = `${proxyBaseUrlNoSuffixedWithSlash}${tarballPathPrefixedWithSlash}${originalSearchParamsStr}`;
197
+ console.log("tarballPathPrefixedWithSlash", tarballPathPrefixedWithSlash);
198
+ console.log("dist.tarballUrl", dist.tarball);
188
199
  }
189
200
  }
190
201
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
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
@@ -1,27 +1,47 @@
1
1
  #!/usr/bin/env node
2
2
  import {createServer, Server as HttpServer, IncomingMessage, ServerResponse, OutgoingHttpHeaders} from 'http';
3
- import { createServer as createHttpsServer, Server as HttpsServer } from 'https';
4
- import { readFileSync, promises as fsPromises } from 'fs';
5
- import { AddressInfo } from 'net';
6
- import { load } from 'js-yaml';
7
- import fetch, { Response } from 'node-fetch';
8
- import { homedir } from 'os';
9
- import { join, resolve } from 'path';
10
- import { URL } from 'url';
11
-
12
- const { readFile, writeFile } = fsPromises;
13
-
14
- interface RegistryConfig { npmAuthToken?: string; }
15
- interface HttpsConfig { key: string; cert: string; }
3
+ import {createServer as createHttpsServer, Server as HttpsServer} from 'https';
4
+ import {readFileSync, promises as fsPromises} from 'fs';
5
+ import {AddressInfo} from 'net';
6
+ import {load} from 'js-yaml';
7
+ import fetch, {Response} from 'node-fetch';
8
+ import {homedir} from 'os';
9
+ import {join, resolve} from 'path';
10
+ import {URL} from 'url';
11
+
12
+ const {readFile, writeFile} = fsPromises;
13
+
14
+ interface RegistryConfig {
15
+ npmAuthToken?: string;
16
+ }
17
+
18
+ interface HttpsConfig {
19
+ key: string;
20
+ cert: string;
21
+ }
22
+
16
23
  interface ProxyConfig {
17
24
  registries: Record<string, RegistryConfig | null>;
18
25
  https?: HttpsConfig;
19
26
  basePath?: string;
20
27
  }
21
- interface YarnConfig { npmRegistries?: Record<string, RegistryConfig | null>; }
22
- interface RegistryInfo { url: string; token?: string; }
23
- interface PackageVersion { dist?: { tarball?: string }; }
24
- interface PackageData { versions?: Record<string, PackageVersion>; }
28
+
29
+ interface YarnConfig {
30
+ npmRegistries?: Record<string, RegistryConfig | null>;
31
+ }
32
+
33
+ interface RegistryInfo {
34
+ url: string;
35
+ token?: string;
36
+ }
37
+
38
+ interface PackageVersion {
39
+ dist?: { tarball?: string };
40
+ }
41
+
42
+ interface PackageData {
43
+ versions?: Record<string, PackageVersion>;
44
+ }
25
45
 
26
46
  class ConcurrencyLimiter {
27
47
  private maxConcurrency: number;
@@ -54,6 +74,12 @@ class ConcurrencyLimiter {
54
74
 
55
75
  const limiter = new ConcurrencyLimiter(3);
56
76
 
77
+ function removeEndingSlashButReserveStartingSlash(str: string): string {
78
+ if (!str) return '/';
79
+ if (str === '/') return '/';
80
+ return str.replace(/\/+$/, '');
81
+ }
82
+
57
83
  function normalizeUrl(url: string): string {
58
84
  try {
59
85
  const urlObj = new URL(url);
@@ -62,7 +88,7 @@ function normalizeUrl(url: string): string {
62
88
  } else if (urlObj.protocol === 'https:' && (urlObj.port === '443' || urlObj.port === '')) {
63
89
  urlObj.port = '';
64
90
  }
65
- urlObj.pathname = urlObj.pathname.replace(/\/+$/, '');
91
+ urlObj.pathname = removeEndingSlashButReserveStartingSlash(urlObj.pathname);
66
92
  return urlObj.toString();
67
93
  } catch (e) {
68
94
  console.error(`Invalid URL: ${url}`, e);
@@ -144,7 +170,7 @@ async function loadRegistries(
144
170
  }
145
171
  }
146
172
  }
147
- registryMap.set(normalizedUrl, { url: normalizedUrl, token });
173
+ registryMap.set(normalizedUrl, {url: normalizedUrl, token});
148
174
  }
149
175
  return Array.from(registryMap.values());
150
176
  }
@@ -184,14 +210,14 @@ export async function startProxyServer(
184
210
  : fullUrl.pathname;
185
211
  console.log(`Proxying: ${relativePathPrefixedWithSlash}`);
186
212
 
187
- const fetchPromises = registries.map(async ({ url, token }) => {
213
+ const fetchPromises = registries.map(async ({url, token}) => {
188
214
  await limiter.acquire();
189
215
  try {
190
216
  const cleanRelativePath = relativePathPrefixedWithSlash.replace(/^\/+|\/+$/g, '');
191
217
  const targetUrl = `${url}/${cleanRelativePath}${fullUrl.search || ''}`;
192
218
  console.log(`Fetching from: ${targetUrl}`);
193
- const headers = token ? { Authorization: `Bearer ${token}` } : undefined;
194
- const response = await fetch(targetUrl, { headers, });
219
+ const headers = token ? {Authorization: `Bearer ${token}`} : undefined;
220
+ const response = await fetch(targetUrl, {headers,});
195
221
  console.log(`Response from ${targetUrl}: ${response.status} ${response.statusText}`);
196
222
  return response.ok ? response : null;
197
223
  } catch (e) {
@@ -215,8 +241,10 @@ export async function startProxyServer(
215
241
  try {
216
242
  const data = await successResponse.json() as PackageData;
217
243
  if (data.versions) {
218
- console.log("Requested url is", req.url);
219
- const proxyBaseUrlNoSuffixedWithSlash = `${proxyConfig.https ? 'https' : 'http'}://${req.headers.host || 'localhost:' + proxyPort}${basePathPrefixedWithSlash==='/'?'':basePathPrefixedWithSlash}`;
244
+ const requestHeadersHostFromYarnClient = req.headers.host || 'localhost:' + proxyPort;
245
+ console.log("Request headers.host from yarn client is", requestHeadersHostFromYarnClient);
246
+ const proxyBaseUrlNoSuffixedWithSlash = `${proxyConfig.https ? 'https' : 'http'}://${requestHeadersHostFromYarnClient}${basePathPrefixedWithSlash === '/' ? '' : basePathPrefixedWithSlash}`;
247
+ console.log("proxyBaseUrlNoSuffixedWithSlash", proxyBaseUrlNoSuffixedWithSlash);
220
248
  for (const version in data.versions) {
221
249
  const dist = data.versions[version]?.dist;
222
250
  if (dist?.tarball) {
@@ -224,10 +252,12 @@ export async function startProxyServer(
224
252
  const originalSearchParamsStr = originalUrl.search || '';
225
253
  const tarballPathPrefixedWithSlash = removeRegistryPrefix(dist.tarball, registries);
226
254
  dist.tarball = `${proxyBaseUrlNoSuffixedWithSlash}${tarballPathPrefixedWithSlash}${originalSearchParamsStr}`;
255
+ console.log("tarballPathPrefixedWithSlash", tarballPathPrefixedWithSlash);
256
+ console.log("dist.tarballUrl", dist.tarball);
227
257
  }
228
258
  }
229
259
  }
230
- res.writeHead(200, { 'Content-Type': 'application/json' });
260
+ res.writeHead(200, {'Content-Type': 'application/json'});
231
261
  res.end(JSON.stringify(data));
232
262
  } catch (e) {
233
263
  console.error('Failed to parse JSON response:', e);
@@ -240,12 +270,12 @@ export async function startProxyServer(
240
270
  return;
241
271
  }
242
272
  const contentLength = successResponse.headers.get('Content-Length');
243
- const safeHeaders:OutgoingHttpHeaders = {
244
- 'Content-Type': successResponse.headers.get('Content-Type')||undefined,
273
+ const safeHeaders: OutgoingHttpHeaders = {
274
+ 'Content-Type': successResponse.headers.get('Content-Type') || undefined,
245
275
  'Content-Length': contentLength && !isNaN(Number(contentLength)) ? contentLength : undefined,
246
276
  };
247
277
  res.writeHead(successResponse.status, safeHeaders);
248
- successResponse.body.pipe(res).on('error', (err:any) => {
278
+ successResponse.body.pipe(res).on('error', (err: any) => {
249
279
  console.error(`Stream error for ${relativePathPrefixedWithSlash}:`, err);
250
280
  res.writeHead(502).end('Stream Error');
251
281
  });
@@ -254,7 +284,7 @@ export async function startProxyServer(
254
284
 
255
285
  let server: HttpServer | HttpsServer;
256
286
  if (proxyConfig.https) {
257
- const { key, cert } = proxyConfig.https;
287
+ const {key, cert} = proxyConfig.https;
258
288
  const keyPath = resolvePath(key);
259
289
  const certPath = resolvePath(cert);
260
290
  try {
@@ -294,7 +324,7 @@ export async function startProxyServer(
294
324
  }
295
325
 
296
326
  if (import.meta.url === `file://${process.argv[1]}`) {
297
- const [,, configPath, localYarnPath, globalYarnPath, port] = process.argv;
327
+ const [, , configPath, localYarnPath, globalYarnPath, port] = process.argv;
298
328
  startProxyServer(
299
329
  configPath,
300
330
  localYarnPath,