com.jimuwd.xian.registry-proxy 1.0.59 → 1.0.61

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.
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { Server as HttpServer } from 'http';
3
+ import { Server as HttpsServer } from 'https';
4
+ export declare function startProxyServer(proxyConfigPath?: string, localYarnConfigPath?: string, globalYarnConfigPath?: string, port?: number): Promise<HttpServer | HttpsServer>;
package/dist/index.js CHANGED
@@ -161,7 +161,7 @@ async function fetchFromRegistry(registry, targetUrl, limiter) {
161
161
  }
162
162
  }
163
163
  // 修改后的 writeSuccessfulResponse 函数
164
- async function writeSuccessfulResponse(registryInfo, targetUrl, res, upstreamResponse, req, proxyInfo, proxyPort, registryInfos) {
164
+ async function writeSuccessfulResponse(registryInfo, targetUrl, resToDownstream, upstreamResponse, downstreamReq, proxyInfo, proxyPort, registryInfos) {
165
165
  if (!upstreamResponse.ok)
166
166
  throw new Error("Only 2xx upstream response is supported");
167
167
  try {
@@ -184,7 +184,7 @@ async function writeSuccessfulResponse(registryInfo, targetUrl, res, upstreamRes
184
184
  // JSON 处理逻辑
185
185
  const data = await upstreamResponse.json();
186
186
  if (data.versions) {
187
- const host = req.headers.host || `localhost:${proxyPort}`;
187
+ const host = downstreamReq.headers.host || `localhost:${proxyPort}`;
188
188
  const baseUrl = `${proxyInfo.https ? 'https' : 'http'}://${host}${proxyInfo.basePath === '/' ? '' : proxyInfo.basePath}`;
189
189
  for (const versionKey in data.versions) {
190
190
  const packageVersion = data.versions[versionKey];
@@ -196,32 +196,46 @@ async function writeSuccessfulResponse(registryInfo, targetUrl, res, upstreamRes
196
196
  }
197
197
  }
198
198
  }
199
- res.writeHead(upstreamResponse.status, { "content-type": contentType }).end(JSON.stringify(data));
199
+ resToDownstream.writeHead(upstreamResponse.status, { "content-type": contentType }).end(JSON.stringify(data));
200
200
  }
201
201
  else {
202
202
  // 二进制流处理
203
203
  if (!upstreamResponse.body) {
204
204
  logger.error(`Empty response body from ${targetUrl}`);
205
- res.writeHead(502).end('Empty Upstream Response');
205
+ resToDownstream.writeHead(502).end('Empty Upstream Response');
206
206
  }
207
207
  else {
208
208
  // write back to client
209
- res.writeHead(upstreamResponse.status, safeHeaders);
209
+ resToDownstream.writeHead(upstreamResponse.status, safeHeaders);
210
210
  // stop transfer if client is closed accidentally.
211
- // req.on('close', () => upstreamResponse.body?.unpipe());
211
+ const cleanup = () => {
212
+ downstreamReq.off('close', cleanup);
213
+ logger.warn("Stop transfer when client is closed accidentally.");
214
+ upstreamResponse.body?.unpipe();
215
+ };
216
+ downstreamReq.on('close', cleanup);
212
217
  // write back body data (chunked probably)
218
+ // pipe upstream body to client
219
+ upstreamResponse.body.pipe(resToDownstream, { end: true });
213
220
  upstreamResponse.body
214
- .on('data', (chunk) => res.write(chunk))
215
- .on('end', () => res.end())
216
- .on('close', () => res.destroy(new Error(`Upstream server ${registryInfo.normalizedRegistryUrl} closed connection while transferring data on url ${targetUrl}`)))
217
- .on('error', (err) => res.destroy(new Error(`Stream error: ${err.message}`, { cause: err, })));
221
+ .on('data', (chunk) => logger.info(`chunk transferred size=${chunk.size}`))
222
+ .on('close', () => {
223
+ const errMsg = `Upstream server ${registryInfo.normalizedRegistryUrl} closed connection while transferring data on url ${targetUrl}`;
224
+ logger.error(errMsg);
225
+ resToDownstream.destroy(new Error(errMsg));
226
+ })
227
+ .on('error', (err) => {
228
+ const errMsg = `Stream error: ${err.message}`;
229
+ logger.error(errMsg);
230
+ resToDownstream.destroy(new Error(errMsg, { cause: err, }));
231
+ });
218
232
  }
219
233
  }
220
234
  }
221
235
  catch (err) {
222
236
  logger.error('Failed to write upstreamResponse:', err);
223
- if (!res.headersSent) {
224
- res.writeHead(502).end('Internal Server Error');
237
+ if (!resToDownstream.headersSent) {
238
+ resToDownstream.writeHead(502).end('Internal Server Error');
225
239
  }
226
240
  }
227
241
  }
@@ -0,0 +1,8 @@
1
+ declare const logger: {
2
+ info: (...args: any[]) => void;
3
+ success: (...args: any[]) => void;
4
+ error: (...args: any[]) => void;
5
+ warn: (...args: any[]) => void;
6
+ debug: (...args: any[]) => void | "" | undefined;
7
+ };
8
+ export default logger;
@@ -0,0 +1,24 @@
1
+ // utils/logger.ts
2
+ const COLORS = {
3
+ reset: '\x1b[0m',
4
+ proxy: '\x1b[36m', // 青色
5
+ success: '\x1b[32m', // 绿色
6
+ error: '\x1b[31m', // 红色
7
+ warn: '\x1b[33m', // 黄色
8
+ debug: '\x1b[35m' // 紫色
9
+ };
10
+ const PREFIX = {
11
+ proxy: `${COLORS.proxy}[PROXY]${COLORS.reset}`,
12
+ error: `${COLORS.error}[ERROR]${COLORS.reset}`,
13
+ warn: `${COLORS.warn}[WARN]${COLORS.reset}`,
14
+ debug: `${COLORS.debug}[DEBUG]${COLORS.reset}`
15
+ };
16
+ // 代理服务器专用日志
17
+ const logger = {
18
+ info: (...args) => console.log(`${PREFIX.proxy}`, ...args),
19
+ success: (...args) => console.log(`${PREFIX.proxy} ${COLORS.success}✓${COLORS.reset}`, ...args),
20
+ error: (...args) => console.error(`${PREFIX.error}`, ...args),
21
+ warn: (...args) => console.warn(`${PREFIX.warn}`, ...args),
22
+ debug: (...args) => process.env.DEBUG && console.debug(`${PREFIX.debug}`, ...args)
23
+ };
24
+ export default logger;
package/package.json CHANGED
@@ -1,12 +1,15 @@
1
1
  {
2
2
  "name": "com.jimuwd.xian.registry-proxy",
3
- "version": "1.0.59",
3
+ "version": "1.0.61",
4
4
  "description": "A lightweight npm registry proxy with fallback support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
8
8
  "registry-proxy": "dist/index.js"
9
9
  },
10
+ "files": [
11
+ "dist"
12
+ ],
10
13
  "scripts": {
11
14
  "build": "tsc",
12
15
  "deploy": "yarn build && yarn npm publish"
package/src/index.ts DELETED
@@ -1,403 +0,0 @@
1
- #!/usr/bin/env node
2
- import {createServer, IncomingMessage, OutgoingHttpHeaders, Server as HttpServer, ServerResponse} from 'http';
3
- import {createServer as createHttpsServer, Server as HttpsServer} from 'https';
4
- import {promises as fsPromises, readFileSync} 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
- import logger from "./utils/logger.js";
12
-
13
- const {readFile, writeFile} = fsPromises;
14
-
15
- interface RegistryConfig {
16
- npmAuthToken?: string;
17
- }
18
-
19
- interface HttpsConfig {
20
- key: string;
21
- cert: string;
22
- }
23
-
24
- interface ProxyConfig {
25
- registries: Record<string, RegistryConfig | null>;
26
- https?: HttpsConfig;
27
- basePath?: string;
28
- }
29
-
30
- interface YarnConfig {
31
- npmRegistries?: Record<string, RegistryConfig | null>;
32
- }
33
-
34
- interface RegistryInfo {
35
- normalizedRegistryUrl: string;
36
- token?: string;
37
- }
38
-
39
- interface ProxyInfo {
40
- registries: RegistryInfo[];
41
- https?: HttpsConfig;
42
- basePath?: string;
43
- }
44
-
45
- interface PackageVersion {
46
- dist?: { tarball?: string };
47
- }
48
-
49
- interface PackageData {
50
- versions?: Record<string, PackageVersion>;
51
- }
52
-
53
- class ConcurrencyLimiter {
54
- private readonly maxConcurrency: number;
55
- private current: number = 0;
56
- private queue: Array<() => void> = [];
57
-
58
- constructor(maxConcurrency: number) {
59
- this.maxConcurrency = maxConcurrency;
60
- }
61
-
62
- async acquire(): Promise<void> {
63
- if (this.current < this.maxConcurrency) {
64
- this.current++;
65
- return Promise.resolve();
66
- }
67
- return new Promise((resolve) => {
68
- this.queue.push(resolve);
69
- });
70
- }
71
-
72
- release(): void {
73
- this.current--;
74
- const next = this.queue.shift();
75
- if (next) {
76
- this.current++;
77
- next();
78
- }
79
- }
80
- }
81
-
82
- const limiter = new ConcurrencyLimiter(10);
83
-
84
- function removeEndingSlashAndForceStartingSlash(str: string | undefined | null): string {
85
- if (!str) return '/';
86
- let trimmed = str.trim();
87
- if (trimmed === '/') return '/';
88
- if (trimmed === '') return '/';
89
- if (!trimmed.startsWith('/')) trimmed = '/' + trimmed;
90
- return trimmed.replace(/\/+$/, '');
91
- }
92
-
93
- function normalizeUrl(httpOrHttpsUrl: string): string {
94
- if (!httpOrHttpsUrl.startsWith("http")) throw new Error("http(s) url must starts with 'http(s)://'");
95
- try {
96
- const urlObj = new URL(httpOrHttpsUrl);
97
- if (urlObj.protocol === 'http:' && (urlObj.port === '80' || urlObj.port === '')) {
98
- urlObj.port = '';
99
- } else if (urlObj.protocol === 'https:' && (urlObj.port === '443' || urlObj.port === '')) {
100
- urlObj.port = '';
101
- }
102
- return urlObj.toString().replace(/\/+$/, '');
103
- } catch (e) {
104
- throw new Error(`Invalid URL: ${httpOrHttpsUrl}`, {cause: e});
105
- }
106
- }
107
-
108
- function resolvePath(path: string): string {
109
- return path.startsWith('~/') ? join(homedir(), path.slice(2)) : resolve(path);
110
- }
111
-
112
- function removeRegistryPrefix(tarballUrl: string, registries: RegistryInfo[]): string {
113
- const normalizedTarball = normalizeUrl(tarballUrl);
114
- const normalizedRegistries = registries
115
- .map(r => normalizeUrl(r.normalizedRegistryUrl))
116
- .sort((a, b) => b.length - a.length);
117
- for (const normalizedRegistry of normalizedRegistries) {
118
- if (normalizedTarball.startsWith(normalizedRegistry)) {
119
- return normalizedTarball.slice(normalizedRegistry.length) || '/';
120
- }
121
- }
122
- throw new Error(`Can't find tarball url ${tarballUrl} does not match given registries ${normalizedRegistries}`)
123
- }
124
-
125
- async function readProxyConfig(proxyConfigPath = './.registry-proxy.yml'): Promise<ProxyConfig> {
126
- const resolvedPath = resolvePath(proxyConfigPath);
127
- try {
128
- const content = await readFile(resolvedPath, 'utf8');
129
- const config = load(content) as ProxyConfig;
130
- if (!config.registries) {
131
- logger.error('Missing required "registries" field in config');
132
- process.exit(1);
133
- }
134
- return config;
135
- } catch (e) {
136
- logger.error(`Failed to load proxy config from ${resolvedPath}:`, e);
137
- process.exit(1);
138
- }
139
- }
140
-
141
- async function readYarnConfig(path: string): Promise<YarnConfig> {
142
- try {
143
- const content = await readFile(resolvePath(path), 'utf8');
144
- return load(content) as YarnConfig;
145
- } catch (e) {
146
- logger.warn(`Failed to load Yarn config from ${path}:`, e);
147
- return {};
148
- }
149
- }
150
-
151
- async function loadProxyInfo(
152
- proxyConfigPath = './.registry-proxy.yml',
153
- localYarnConfigPath = './.yarnrc.yml',
154
- globalYarnConfigPath = join(homedir(), '.yarnrc.yml')
155
- ): Promise<ProxyInfo> {
156
- const [proxyConfig, localYarnConfig, globalYarnConfig] = await Promise.all([
157
- readProxyConfig(proxyConfigPath),
158
- readYarnConfig(localYarnConfigPath),
159
- readYarnConfig(globalYarnConfigPath)
160
- ]);
161
- const registryMap = new Map<string, RegistryInfo>();
162
- for (const [proxiedRegUrl, proxyRegConfig] of Object.entries(proxyConfig.registries)) {
163
- const normalizedProxiedRegUrl = normalizeUrl(proxiedRegUrl);
164
- let token = proxyRegConfig?.npmAuthToken;
165
- if (!token) {
166
- const yarnConfigs = [localYarnConfig, globalYarnConfig];
167
- for (const yarnConfig of yarnConfigs) {
168
- if (yarnConfig.npmRegistries) {
169
- const foundEntry = Object.entries(yarnConfig.npmRegistries)
170
- .find(([registryUrl]) => normalizedProxiedRegUrl === normalizeUrl(registryUrl))
171
- if (foundEntry) {
172
- const [, registryConfig] = foundEntry;
173
- if (registryConfig?.npmAuthToken) {
174
- token = registryConfig.npmAuthToken;
175
- break;
176
- }
177
- }
178
- }
179
- }
180
- }
181
- registryMap.set(normalizedProxiedRegUrl, {normalizedRegistryUrl: normalizedProxiedRegUrl, token});
182
- }
183
- const registries = Array.from(registryMap.values());
184
- const https = proxyConfig.https;
185
- const basePath = removeEndingSlashAndForceStartingSlash(proxyConfig.basePath);
186
- return {registries, https, basePath};
187
- }
188
-
189
- async function fetchFromRegistry(
190
- registry: RegistryInfo,
191
- targetUrl: string,
192
- limiter: ConcurrencyLimiter
193
- ): Promise<Response | null> {
194
- await limiter.acquire();
195
- try {
196
- logger.info(`Fetching from: ${targetUrl}`);
197
- const headers: {} = registry.token ? {Authorization: `Bearer ${registry.token}`} : {};
198
- (headers as any).Collection = "keep-alive";
199
- const response = await fetch(targetUrl, {headers});
200
- logger.info(`Response from upstream ${targetUrl}: ${response.status} ${response.statusText} content-type=${response.headers.get('content-type')} content-length=${response.headers.get('content-length')} transfer-encoding=${response.headers.get('transfer-encoding')}`);
201
- return response.ok ? response : null;
202
- } catch (e) {
203
- if (e instanceof Error) {
204
- logger.error(
205
- (e as any).code === 'ECONNREFUSED'
206
- ? `Registry ${registry.normalizedRegistryUrl} unreachable [ECONNREFUSED]`
207
- : `Error from ${registry.normalizedRegistryUrl}: ${e.message}`
208
- );
209
- }
210
- return null;
211
- } finally {
212
- limiter.release();
213
- }
214
- }
215
-
216
- // 修改后的 writeSuccessfulResponse 函数
217
- async function writeSuccessfulResponse(
218
- registryInfo: RegistryInfo,
219
- targetUrl: string,
220
- res: ServerResponse,
221
- upstreamResponse: Response,
222
- req: IncomingMessage,
223
- proxyInfo: ProxyInfo,
224
- proxyPort: number,
225
- registryInfos: RegistryInfo[]
226
- ): Promise<void> {
227
-
228
- if (!upstreamResponse.ok) throw new Error("Only 2xx upstream response is supported");
229
-
230
- try {
231
- const contentType = upstreamResponse.headers.get('content-type') || 'application/octet-stream';
232
- const connection = upstreamResponse.headers.get("connection") || 'keep-alive';
233
-
234
- // 准备通用头信息
235
- const safeHeaders: OutgoingHttpHeaders = {'connection': connection, 'content-type': contentType,};
236
-
237
- // 复制所有可能需要的头信息
238
- const headersToCopy = [
239
- 'content-length',
240
- 'content-encoding',
241
- 'transfer-encoding',
242
- ];
243
-
244
- headersToCopy.forEach(header => {
245
- const value = upstreamResponse.headers.get(header);
246
- if (value) safeHeaders[header] = value;
247
- });
248
-
249
- if (contentType.includes('application/json')) {
250
- // JSON 处理逻辑
251
- const data = await upstreamResponse.json() as PackageData;
252
- if (data.versions) {
253
- const host = req.headers.host || `localhost:${proxyPort}`;
254
- const baseUrl = `${proxyInfo.https ? 'https' : 'http'}://${host}${proxyInfo.basePath === '/' ? '' : proxyInfo.basePath}`;
255
-
256
- for (const versionKey in data.versions) {
257
- const packageVersion = data.versions[versionKey];
258
- const tarball = packageVersion?.dist?.tarball;
259
- if (tarball) {
260
- const path = removeRegistryPrefix(tarball, registryInfos);
261
- const proxiedTarballUrl: string = `${baseUrl}${path}${new URL(tarball).search || ''}`;
262
- packageVersion!.dist!.tarball = proxiedTarballUrl as string;
263
- }
264
- }
265
- }
266
- res.writeHead(upstreamResponse.status, {"content-type": contentType}).end(JSON.stringify(data));
267
- } else {
268
- // 二进制流处理
269
- if (!upstreamResponse.body) {
270
- logger.error(`Empty response body from ${targetUrl}`);
271
- res.writeHead(502).end('Empty Upstream Response');
272
- } else {
273
- // write back to client
274
- res.writeHead(upstreamResponse.status, safeHeaders);
275
- // stop transfer if client is closed accidentally.
276
- // req.on('close', () => upstreamResponse.body?.unpipe());
277
- // write back body data (chunked probably)
278
- upstreamResponse.body
279
- .on('data', (chunk) => res.write(chunk))
280
- .on('end', () => res.end())
281
- .on('close', () => res.destroy(new Error(`Upstream server ${registryInfo.normalizedRegistryUrl} closed connection while transferring data on url ${targetUrl}`)))
282
- .on('error', (err: Error) => res.destroy(new Error(`Stream error: ${err.message}`, {cause: err,})));
283
- }
284
- }
285
- } catch (err) {
286
- logger.error('Failed to write upstreamResponse:', err);
287
- if (!res.headersSent) {
288
- res.writeHead(502).end('Internal Server Error');
289
- }
290
- }
291
- }
292
-
293
- export async function startProxyServer(
294
- proxyConfigPath?: string,
295
- localYarnConfigPath?: string,
296
- globalYarnConfigPath?: string,
297
- port: number = 0
298
- ): Promise<HttpServer | HttpsServer> {
299
- const proxyInfo = await loadProxyInfo(proxyConfigPath, localYarnConfigPath, globalYarnConfigPath);
300
- const registryInfos = proxyInfo.registries;
301
- const basePathPrefixedWithSlash: string = removeEndingSlashAndForceStartingSlash(proxyInfo.basePath);
302
-
303
- logger.info('Active registries:', registryInfos.map(r => r.normalizedRegistryUrl));
304
- logger.info('Proxy base path:', basePathPrefixedWithSlash);
305
- logger.info('HTTPS:', !!proxyInfo.https);
306
-
307
- let proxyPort: number;
308
-
309
- const requestHandler = async (req: IncomingMessage, res: ServerResponse) => {
310
- if (!req.url || !req.headers.host) {
311
- res.writeHead(400).end('Invalid Request');
312
- return;
313
- }
314
-
315
- const fullUrl = new URL(req.url, `${proxyInfo.https ? 'https' : 'http'}://${req.headers.host}`);
316
- if (!fullUrl.pathname.startsWith(basePathPrefixedWithSlash)) {
317
- res.writeHead(404).end('Not Found');
318
- return;
319
- }
320
-
321
- const path = basePathPrefixedWithSlash === '/'
322
- ? fullUrl.pathname
323
- : fullUrl.pathname.slice(basePathPrefixedWithSlash.length);
324
-
325
- // 顺序尝试注册表,获取第一个成功响应
326
- let successfulResponse: Response | null = null;
327
- let targetRegistry: RegistryInfo | null = null;
328
- let targetUrl: string | null = null;
329
- for (const registry of registryInfos) {
330
- if (req.destroyed) break;
331
- targetRegistry = registry;
332
- const search = fullUrl.search || '';
333
- targetUrl = `${registry.normalizedRegistryUrl}${path}${search}`;
334
- const okResponseOrNull = await fetchFromRegistry(registry, targetUrl, limiter);
335
- if (okResponseOrNull) {
336
- successfulResponse = okResponseOrNull;
337
- break;
338
- }
339
- }
340
-
341
- // 统一回写响应
342
- if (successfulResponse) {
343
- await writeSuccessfulResponse(targetRegistry!, targetUrl!, res, successfulResponse, req, proxyInfo, proxyPort, registryInfos);
344
- } else {
345
- res.writeHead(404).end('All upstream registries failed');
346
- }
347
- };
348
-
349
- let server: HttpServer | HttpsServer;
350
- if (proxyInfo.https) {
351
- const {key, cert} = proxyInfo.https;
352
- const keyPath = resolvePath(key);
353
- const certPath = resolvePath(cert);
354
- try {
355
- await fsPromises.access(keyPath);
356
- await fsPromises.access(certPath);
357
- } catch (e) {
358
- logger.error(`HTTPS config error: key or cert file not found`, e);
359
- process.exit(1);
360
- }
361
- const httpsOptions = {
362
- key: readFileSync(keyPath),
363
- cert: readFileSync(certPath),
364
- };
365
- server = createHttpsServer(httpsOptions, requestHandler);
366
- } else {
367
- server = createServer(requestHandler);
368
- }
369
-
370
- const promisedServer: Promise<HttpServer | HttpsServer> = new Promise((resolve, reject) => {
371
- server.on('error', (err: NodeJS.ErrnoException) => {
372
- if (err.code === 'EADDRINUSE') {
373
- logger.error(`Port ${port} is in use, please specify a different port or free it.`);
374
- process.exit(1);
375
- }
376
- logger.error('Server error:', err);
377
- reject(err);
378
- });
379
- server.listen(port, () => {
380
- const address = server.address() as AddressInfo;
381
- proxyPort = address.port;
382
- const portFile = join(process.env.PROJECT_ROOT || process.cwd(), '.registry-proxy-port');
383
- writeFile(portFile, proxyPort.toString()).catch(e => logger.error('Failed to write port file:', e));
384
- logger.info(`Proxy server running on ${proxyInfo.https ? 'https' : 'http'}://localhost:${proxyPort}${basePathPrefixedWithSlash === '/' ? '' : basePathPrefixedWithSlash}`);
385
- resolve(server);
386
- });
387
- });
388
-
389
- return promisedServer as Promise<HttpServer | HttpsServer>;
390
- }
391
-
392
- if (import.meta.url === `file://${process.argv[1]}`) {
393
- const [, , configPath, localYarnPath, globalYarnPath, port] = process.argv;
394
- startProxyServer(
395
- configPath,
396
- localYarnPath,
397
- globalYarnPath,
398
- parseInt(port, 10) || 0
399
- ).catch(err => {
400
- logger.error('Failed to start server:', err);
401
- process.exit(1);
402
- });
403
- }
@@ -1,36 +0,0 @@
1
- // utils/logger.ts
2
- const COLORS = {
3
- reset: '\x1b[0m',
4
- proxy: '\x1b[36m', // 青色
5
- success: '\x1b[32m', // 绿色
6
- error: '\x1b[31m', // 红色
7
- warn: '\x1b[33m', // 黄色
8
- debug: '\x1b[35m' // 紫色
9
- };
10
-
11
- const PREFIX = {
12
- proxy: `${COLORS.proxy}[PROXY]${COLORS.reset}`,
13
- error: `${COLORS.error}[ERROR]${COLORS.reset}`,
14
- warn: `${COLORS.warn}[WARN]${COLORS.reset}`,
15
- debug: `${COLORS.debug}[DEBUG]${COLORS.reset}`
16
- };
17
-
18
- // 代理服务器专用日志
19
- const logger = {
20
- info: (...args: any[]) =>
21
- console.log(`${PREFIX.proxy}`, ...args),
22
-
23
- success: (...args: any[]) =>
24
- console.log(`${PREFIX.proxy} ${COLORS.success}✓${COLORS.reset}`, ...args),
25
-
26
- error: (...args: any[]) =>
27
- console.error(`${PREFIX.error}`, ...args),
28
-
29
- warn: (...args: any[]) =>
30
- console.warn(`${PREFIX.warn}`, ...args),
31
-
32
- debug: (...args: any[]) =>
33
- process.env.DEBUG && console.debug(`${PREFIX.debug}`, ...args)
34
- };
35
-
36
- export default logger;
package/tsconfig.json DELETED
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "esnext",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "strict": true,
9
- "esModuleInterop": true,
10
- "skipLibCheck": true,
11
- "declaration": true
12
- },
13
- "include": ["src/**/*"]
14
- }