@robiki/proxy 1.0.3 → 1.0.4

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/README.de.md CHANGED
@@ -18,7 +18,7 @@
18
18
  - **✅ Anforderungsvalidierung**: Benutzerdefinierte Validierungslogik für Authentifizierung, Rate Limiting usw.
19
19
  - **🔄 URL-Remapping**: URLs vor der Weiterleitung an Zieldienste transformieren
20
20
  - **📦 Duale Nutzung**: Als npm-Paket oder Docker-Container verwenden
21
- - **⚙️ JavaScript & TypeScript-Konfigurationsunterstützung**: Verwenden Sie `.cjs` oder `.ts`-Konfigurationsdateien mit Funktionen in Docker
21
+ - **⚙️ JavaScript & TypeScript-Konfigurationsunterstützung**: Verwenden Sie `.js` oder `.ts`-Konfigurationsdateien mit Funktionen in Docker
22
22
  - **🎯 Multi-Port-Unterstützung**: Gleichzeitiges Lauschen auf mehreren Ports
23
23
  - **⚡ Hohe Leistung**: Basiert auf der nativen HTTP/2-Implementierung von Node.js
24
24
 
@@ -145,8 +145,8 @@ Einfache deklarative Konfiguration:
145
145
  Für erweiterte Funktionen wie URL-Remapping und Validierung:
146
146
 
147
147
  ```javascript
148
- // proxy.config.cjs
149
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
150
150
  ports: [443, 8080],
151
151
  ssl: {
152
152
  key: './certs/key.pem',
package/README.es.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - **✅ Validación de solicitudes**: Lógica de validación personalizada para autenticación, limitación de velocidad, etc.
19
19
  - **🔄 Remapeo de URL**: Transforma URLs antes de reenviarlas a servicios de destino
20
20
  - **📦 Uso dual**: Usa como paquete npm o contenedor Docker
21
+ - **⚙️ Soporte de configuración JavaScript y TypeScript**: Usa archivos de configuración `.js` o `.ts` con funciones en Docker
21
22
  - **🎯 Soporte multi-puerto**: Escucha en múltiples puertos simultáneamente
22
23
  - **⚡ Alto rendimiento**: Construido sobre la implementación nativa HTTP/2 de Node.js
23
24
 
@@ -144,8 +145,8 @@ Configuración declarativa simple:
144
145
  Para características avanzadas como remapeo de URL y validación:
145
146
 
146
147
  ```javascript
147
- // proxy.config.cjs
148
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
149
150
  ports: [443, 8080],
150
151
  ssl: {
151
152
  key: './certs/key.pem',
package/README.ja.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - **✅ リクエスト検証**:認証、レート制限などのカスタム検証ロジック
19
19
  - **🔄 URL リマッピング**:ターゲットサービスに転送する前に URL を変換
20
20
  - **📦 デュアル使用**:npm パッケージまたは Docker コンテナとして使用
21
+ - **⚙️ JavaScript & TypeScript 設定サポート**:Docker で関数を使用した `.js` または `.ts` 設定ファイルを使用
21
22
  - **🎯 マルチポートサポート**:複数のポートで同時にリッスン
22
23
  - **⚡ 高性能**:Node.js ネイティブ HTTP/2 実装に基づく
23
24
 
@@ -144,8 +145,8 @@ docker-compose up -d
144
145
  URL リマッピングや検証などの高度な機能用:
145
146
 
146
147
  ```javascript
147
- // proxy.config.cjs
148
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
149
150
  ports: [443, 8080],
150
151
  ssl: {
151
152
  key: './certs/key.pem',
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
  - **✅ Request Validation**: Custom validation logic for authentication, rate limiting, etc.
19
19
  - **🔄 URL Remapping**: Transform URLs before forwarding to target services
20
20
  - **📦 Dual Usage**: Use as npm package or Docker container
21
- - **⚙️ JavaScript & TypeScript Config Support**: Use `.cjs` or `.ts` config files with functions in Docker
21
+ - **⚙️ JavaScript & TypeScript Config Support**: Use `.js` or `.ts` config files with functions in Docker
22
22
  - **🎯 Multi-Port Support**: Listen on multiple ports simultaneously
23
23
  - **⚡ High Performance**: Built on Node.js native HTTP/2 implementation
24
24
 
@@ -145,8 +145,8 @@ Simple declarative configuration:
145
145
  For advanced features like URL remapping and validation:
146
146
 
147
147
  ```javascript
148
- // proxy.config.cjs
149
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
150
150
  ports: [443, 8080],
151
151
  ssl: {
152
152
  key: './certs/key.pem',
@@ -262,17 +262,17 @@ interface RouteConfig {
262
262
 
263
263
  ## 🐳 Docker Usage
264
264
 
265
- Mount your config file (JSON, .cjs, or .ts):
265
+ Mount your config file (JSON, .js, or .ts):
266
266
 
267
267
  ```yaml
268
268
  services:
269
269
  proxy:
270
270
  image: robiki/proxy:latest
271
271
  volumes:
272
- - ./proxy.config.cjs:/app/proxy.config.cjs:ro
273
- - ./certs:/app/certs:ro
272
+ - ./proxy.config.js:/usr/src/proxy.config.js:ro
273
+ - ./certs:/usr/src/certs:ro
274
274
  environment:
275
- - PROXY_CONFIG=/app/proxy.config.cjs
275
+ - PROXY_CONFIG=/usr/src/proxy.config.js
276
276
  ```
277
277
 
278
278
  ## 🔐 SSL Certificates
package/README.pl.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - **✅ Walidacja żądań**: Niestandardowa logika walidacji dla uwierzytelniania, limitowania żądań itp.
19
19
  - **🔄 Przekierowywanie URL**: Transformacja URL przed przekazaniem do usług docelowych
20
20
  - **📦 Podwójne użycie**: Użyj jako pakiet npm lub kontener Docker
21
+ - **⚙️ Obsługa konfiguracji JavaScript i TypeScript**: Użyj plików konfiguracyjnych `.js` lub `.ts` z funkcjami w Docker
21
22
  - **🎯 Obsługa wielu portów**: Nasłuchiwanie na wielu portach jednocześnie
22
23
  - **⚡ Wysoka wydajność**: Zbudowany na natywnej implementacji HTTP/2 w Node.js
23
24
 
@@ -144,8 +145,8 @@ Prosta deklaratywna konfiguracja:
144
145
  Dla zaawansowanych funkcji jak przekierowywanie URL i walidacja:
145
146
 
146
147
  ```javascript
147
- // proxy.config.cjs
148
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
149
150
  ports: [443, 8080],
150
151
  ssl: {
151
152
  key: './certs/key.pem',
package/README.ru.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - **✅ Валидация запросов**: Пользовательская логика валидации для аутентификации, ограничения скорости и т.д.
19
19
  - **🔄 Переназначение URL**: Преобразование URL перед пересылкой к целевым сервисам
20
20
  - **📦 Двойное использование**: Используйте как npm-пакет или Docker-контейнер
21
+ - **⚙️ Поддержка конфигурации JavaScript и TypeScript**: Используйте файлы конфигурации `.js` или `.ts` с функциями в Docker
21
22
  - **🎯 Поддержка нескольких портов**: Прослушивание нескольких портов одновременно
22
23
  - **⚡ Высокая производительность**: Построен на нативной реализации HTTP/2 в Node.js
23
24
 
@@ -144,8 +145,8 @@ docker-compose up -d
144
145
  Для расширенных функций, таких как переназначение URL и валидация:
145
146
 
146
147
  ```javascript
147
- // proxy.config.cjs
148
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
149
150
  ports: [443, 8080],
150
151
  ssl: {
151
152
  key: './certs/key.pem',
package/README.zh.md CHANGED
@@ -18,6 +18,7 @@
18
18
  - **✅ 请求验证**:用于身份验证、速率限制等的自定义验证逻辑
19
19
  - **🔄 URL 重映射**:在转发到目标服务之前转换 URL
20
20
  - **📦 双重用途**:可作为 npm 包或 Docker 容器使用
21
+ - **⚙️ JavaScript 和 TypeScript 配置支持**:在 Docker 中使用带有函数的 `.js` 或 `.ts` 配置文件
21
22
  - **🎯 多端口支持**:同时监听多个端口
22
23
  - **⚡ 高性能**:基于 Node.js 原生 HTTP/2 实现
23
24
 
@@ -144,8 +145,8 @@ docker-compose up -d
144
145
  用于高级功能,如 URL 重映射和验证:
145
146
 
146
147
  ```javascript
147
- // proxy.config.cjs
148
- module.exports = {
148
+ // proxy.config.js
149
+ export default {
149
150
  ports: [443, 8080],
150
151
  ssl: {
151
152
  key: './certs/key.pem',
@@ -0,0 +1,168 @@
1
+ import { OutgoingHttpHeaders, IncomingMessage } from 'node:http';
2
+ import { IncomingHttpHeaders } from 'node:http2';
3
+ import { TLSSocket } from 'node:tls';
4
+ import WebSocket from 'ws';
5
+
6
+ declare enum RequestType {
7
+ API = "api",
8
+ STREAM = "stream",
9
+ WEBSOCKET = "websocket"
10
+ }
11
+ interface TLSWebSocket extends WebSocket {
12
+ _socket: TLSSocket;
13
+ }
14
+ type Router = (req: any, res: any) => void;
15
+ type WebSocketRouter = (req: IncomingMessage, socket: TLSWebSocket, headers: IncomingHttpHeaders) => void;
16
+ type Streamer = (stream: any, headers: any, flags: any) => void;
17
+ interface ForwardValidationResult {
18
+ status: boolean;
19
+ message?: string;
20
+ code?: number;
21
+ headers?: OutgoingHttpHeaders;
22
+ }
23
+ interface ConnectionInfo {
24
+ id: number;
25
+ method: string;
26
+ path: string;
27
+ remoteAddress: string;
28
+ scheme: string;
29
+ authority: string;
30
+ origin: string;
31
+ headers: IncomingHttpHeaders;
32
+ query: URLSearchParams;
33
+ type: RequestType;
34
+ respond: (status: number, headers?: Record<string, string>, body?: string) => void;
35
+ end: (body?: string) => void;
36
+ }
37
+
38
+ /**
39
+ * Route configuration for a specific domain/host
40
+ */
41
+ interface RouteConfig {
42
+ /** Target host:port to proxy to */
43
+ target: string;
44
+ /** Enable SSL/TLS for the target */
45
+ ssl?: boolean;
46
+ /** Remap the URL path before forwarding */
47
+ remap?: (url: string) => string;
48
+ /** Custom CORS configuration */
49
+ cors?: CorsConfig;
50
+ /** Validation function for this route */
51
+ validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
52
+ }
53
+ /**
54
+ * CORS configuration
55
+ */
56
+ interface CorsConfig {
57
+ /** Allowed origins (array of strings or '*' for all) */
58
+ origin?: string | string[];
59
+ /** Allowed HTTP methods */
60
+ methods?: string[];
61
+ /** Allowed headers */
62
+ allowedHeaders?: string[];
63
+ /** Exposed headers */
64
+ exposedHeaders?: string[];
65
+ /** Allow credentials */
66
+ credentials?: boolean;
67
+ /** Max age for preflight cache */
68
+ maxAge?: number;
69
+ }
70
+ /**
71
+ * SSL/TLS certificate configuration
72
+ */
73
+ interface CertificateConfig {
74
+ /** Path to private key file or key content */
75
+ key: string;
76
+ /** Path to certificate file or cert content */
77
+ cert: string;
78
+ /** Path to CA file or CA content */
79
+ ca?: string;
80
+ /** Allow HTTP/1.1 fallback */
81
+ allowHTTP1?: boolean;
82
+ }
83
+ /**
84
+ * Server configuration
85
+ */
86
+ interface ServerConfig {
87
+ /** SSL/TLS certificate configuration */
88
+ ssl?: CertificateConfig;
89
+ /** Route configurations mapped by host */
90
+ routes: Record<string, RouteConfig>;
91
+ /** Default CORS configuration */
92
+ cors?: CorsConfig;
93
+ /** Global validation function */
94
+ validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
95
+ /** Ports to listen on (defaults to [443, 8080, 9229]) */
96
+ ports?: number[];
97
+ }
98
+ /**
99
+ * Proxy configuration manager
100
+ */
101
+ declare class ProxyConfig {
102
+ private config;
103
+ private sslConfig?;
104
+ constructor(config: ServerConfig);
105
+ /**
106
+ * Initialize SSL configuration
107
+ */
108
+ private initializeSSL;
109
+ /**
110
+ * Get SSL configuration
111
+ */
112
+ getSSL(): {
113
+ key: Buffer;
114
+ cert: Buffer;
115
+ ca?: Buffer;
116
+ allowHTTP1?: boolean;
117
+ } | undefined;
118
+ /**
119
+ * Get route configuration for a host
120
+ */
121
+ getRoute(host: string): RouteConfig | undefined;
122
+ /**
123
+ * Get target for a host
124
+ */
125
+ getTarget(host: string): {
126
+ target: undefined;
127
+ ssl: undefined;
128
+ remap: undefined;
129
+ } | {
130
+ target: string;
131
+ ssl: {
132
+ key: Buffer;
133
+ cert: Buffer;
134
+ ca?: Buffer;
135
+ allowHTTP1?: boolean;
136
+ } | undefined;
137
+ remap: ((url: string) => string) | undefined;
138
+ };
139
+ /**
140
+ * Get CORS headers for a request
141
+ */
142
+ getCorsHeaders(origin: string, host?: string): OutgoingHttpHeaders;
143
+ /**
144
+ * Validate a request
145
+ */
146
+ validate(info: ConnectionInfo): Promise<ForwardValidationResult>;
147
+ /**
148
+ * Get ports to listen on
149
+ */
150
+ getPorts(): number[];
151
+ /**
152
+ * Get the full configuration
153
+ */
154
+ getConfig(): ServerConfig;
155
+ }
156
+ /**
157
+ * Load configuration with cascading priority (async):
158
+ * 1. Programmatic config (highest priority)
159
+ * 2. Environment variables
160
+ * 3. Config file
161
+ * 4. Defaults (lowest priority)
162
+ *
163
+ * Supports config file types: .json, .ts, .js
164
+ */
165
+ declare function loadConfig(programmaticConfig?: Partial<ServerConfig>): Promise<ProxyConfig>;
166
+
167
+ export { ProxyConfig as P, RequestType as e, loadConfig as l };
168
+ export type { CorsConfig as C, ForwardValidationResult as F, Router as R, ServerConfig as S, TLSWebSocket as T, WebSocketRouter as W, Streamer as a, RouteConfig as b, CertificateConfig as c, ConnectionInfo as d };
@@ -0,0 +1,315 @@
1
+ import { stat, readFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+
4
+ function debug(message, data) {
5
+ if (process.env.DEBUG === "true") {
6
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
7
+ if (data !== void 0) {
8
+ const sanitized = JSON.stringify(data, (_, value) => typeof value === "function" ? "<function>" : value, 2);
9
+ return console.log(`[${timestamp}] [CONFIG] ${message}`, sanitized);
10
+ }
11
+ return console.log(`[${timestamp}] [CONFIG] ${message}`);
12
+ }
13
+ }
14
+
15
+ function loadSSLKey(keyConfig) {
16
+ debug(`Loading SSL key from: ${keyConfig.includes("-----BEGIN") ? "inline" : keyConfig}`);
17
+ return keyConfig.includes("-----BEGIN") ? Promise.resolve(Buffer.from(keyConfig)) : readFile(resolve(keyConfig));
18
+ }
19
+ function loadSSLCert(certConfig) {
20
+ debug(`Loading SSL cert from: ${certConfig.includes("-----BEGIN") ? "inline" : certConfig}`);
21
+ return certConfig.includes("-----BEGIN") ? Promise.resolve(Buffer.from(certConfig)) : readFile(resolve(certConfig));
22
+ }
23
+ function loadSSLCA(caConfig) {
24
+ if (!caConfig) {
25
+ debug("Loading SSL CA from: none");
26
+ return Promise.resolve(void 0);
27
+ }
28
+ debug(`Loading SSL CA from: ${caConfig.includes("-----BEGIN") ? "inline" : caConfig}`);
29
+ return caConfig.includes("-----BEGIN") ? Promise.resolve(Buffer.from(caConfig)) : readFile(resolve(caConfig));
30
+ }
31
+ function initializeSSL(sslConfig) {
32
+ debug("Initializing SSL configuration...");
33
+ return Promise.all([loadSSLKey(sslConfig.key), loadSSLCert(sslConfig.cert), loadSSLCA(sslConfig.ca)]).then(([key, cert, ca]) => {
34
+ const loaded = {
35
+ key,
36
+ cert,
37
+ ca,
38
+ allowHTTP1: sslConfig.allowHTTP1 ?? true
39
+ };
40
+ debug("SSL configuration loaded successfully", {
41
+ keySize: key.length,
42
+ certSize: cert.length,
43
+ caSize: ca?.length || 0,
44
+ allowHTTP1: loaded.allowHTTP1
45
+ });
46
+ return loaded;
47
+ }).catch((error) => {
48
+ debug("Failed to load SSL certificates", error);
49
+ console.error("Failed to load SSL certificates:", error);
50
+ throw error;
51
+ });
52
+ }
53
+ function getRoute(config, host) {
54
+ if (config.routes[host]) {
55
+ return config.routes[host];
56
+ }
57
+ const hostWithoutPort = host.split(":")[0];
58
+ if (config.routes[hostWithoutPort]) {
59
+ return config.routes[hostWithoutPort];
60
+ }
61
+ for (const [pattern, route] of Object.entries(config.routes)) {
62
+ if (pattern.includes("*")) {
63
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
64
+ if (regex.test(host) || regex.test(hostWithoutPort)) {
65
+ return route;
66
+ }
67
+ }
68
+ }
69
+ return void 0;
70
+ }
71
+ function getTarget(config, sslConfig, host) {
72
+ const route = getRoute(config, host);
73
+ if (!route) {
74
+ return { target: void 0, ssl: void 0, remap: void 0 };
75
+ }
76
+ return {
77
+ target: route.target,
78
+ ssl: route.ssl ? sslConfig : void 0,
79
+ remap: route.remap
80
+ };
81
+ }
82
+ function getCorsHeaders(config, origin, host) {
83
+ const route = host ? getRoute(config, host) : void 0;
84
+ const corsConfig = route?.cors || config.cors;
85
+ if (!corsConfig) {
86
+ return {
87
+ "access-control-allow-origin": origin,
88
+ "access-control-allow-methods": "*",
89
+ "access-control-allow-headers": "*",
90
+ "access-control-allow-credentials": "true"
91
+ };
92
+ }
93
+ const headers = {};
94
+ if (corsConfig.origin === "*") {
95
+ headers["access-control-allow-origin"] = "*";
96
+ } else if (Array.isArray(corsConfig.origin)) {
97
+ if (corsConfig.origin.includes(origin)) {
98
+ headers["access-control-allow-origin"] = origin;
99
+ }
100
+ } else if (corsConfig.origin) {
101
+ headers["access-control-allow-origin"] = corsConfig.origin;
102
+ } else {
103
+ headers["access-control-allow-origin"] = origin;
104
+ }
105
+ if (corsConfig.methods) {
106
+ headers["access-control-allow-methods"] = corsConfig.methods.join(", ");
107
+ } else {
108
+ headers["access-control-allow-methods"] = "*";
109
+ }
110
+ if (corsConfig.allowedHeaders) {
111
+ headers["access-control-allow-headers"] = corsConfig.allowedHeaders.join(", ");
112
+ } else {
113
+ headers["access-control-allow-headers"] = "*";
114
+ }
115
+ if (corsConfig.exposedHeaders) {
116
+ headers["access-control-expose-headers"] = corsConfig.exposedHeaders.join(", ");
117
+ }
118
+ if (corsConfig.credentials !== void 0) {
119
+ headers["access-control-allow-credentials"] = corsConfig.credentials ? "true" : "false";
120
+ } else {
121
+ headers["access-control-allow-credentials"] = "true";
122
+ }
123
+ if (corsConfig.maxAge) {
124
+ headers["access-control-max-age"] = corsConfig.maxAge.toString();
125
+ }
126
+ return headers;
127
+ }
128
+ function validateRequest(config, info) {
129
+ const route = getRoute(config, info.authority);
130
+ if (route?.validate) return route.validate(info);
131
+ if (config.validate) return config.validate(info);
132
+ return Promise.resolve({ status: true });
133
+ }
134
+ function getPorts(config) {
135
+ return config.ports || [443, 8080, 9229];
136
+ }
137
+ function createProxyConfig(config, sslConfig) {
138
+ return {
139
+ config,
140
+ sslConfig,
141
+ getSSL: () => sslConfig,
142
+ getRoute: (host) => getRoute(config, host),
143
+ getTarget: (host) => getTarget(config, sslConfig, host),
144
+ getCorsHeaders: (origin, host) => getCorsHeaders(config, origin, host),
145
+ validate: (info) => validateRequest(config, info),
146
+ getPorts: () => getPorts(config),
147
+ getConfig: () => config
148
+ };
149
+ }
150
+ function deepMerge(...objects) {
151
+ const result = {};
152
+ for (const obj of objects) {
153
+ if (!obj) continue;
154
+ for (const key in obj) {
155
+ if (obj[key] === void 0) continue;
156
+ if (typeof obj[key] === "object" && !Array.isArray(obj[key]) && obj[key] !== null) {
157
+ result[key] = deepMerge(result[key] || {}, obj[key]);
158
+ } else {
159
+ result[key] = obj[key];
160
+ }
161
+ }
162
+ }
163
+ return result;
164
+ }
165
+ function getConfigFromEnv() {
166
+ debug("Loading configuration from environment variables...");
167
+ const config = {};
168
+ if (process.env.SSL_KEY && process.env.SSL_CERT) {
169
+ debug("Found SSL configuration in environment variables");
170
+ config.ssl = {
171
+ key: process.env.SSL_KEY,
172
+ cert: process.env.SSL_CERT,
173
+ ca: process.env.SSL_CA,
174
+ allowHTTP1: process.env.SSL_ALLOW_HTTP1 === "true"
175
+ };
176
+ }
177
+ if (process.env.CORS_ORIGIN) {
178
+ debug("Found CORS configuration in environment variables", {
179
+ origin: process.env.CORS_ORIGIN,
180
+ methods: process.env.CORS_METHODS,
181
+ headers: process.env.CORS_HEADERS,
182
+ credentials: process.env.CORS_CREDENTIALS
183
+ });
184
+ config.cors = {
185
+ origin: process.env.CORS_ORIGIN === "*" ? "*" : process.env.CORS_ORIGIN.split(","),
186
+ methods: process.env.CORS_METHODS?.split(","),
187
+ allowedHeaders: process.env.CORS_HEADERS?.split(","),
188
+ credentials: process.env.CORS_CREDENTIALS === "true"
189
+ };
190
+ }
191
+ if (Object.keys(config).length === 0) {
192
+ debug("No configuration found in environment variables");
193
+ }
194
+ return config;
195
+ }
196
+ async function getConfigFromFile() {
197
+ const configPath = process.env.PROXY_CONFIG || "./proxy.config.json";
198
+ const resolvedPath = resolve(configPath);
199
+ debug(`Looking for config file at: ${resolvedPath}`);
200
+ try {
201
+ const stats = await stat(resolvedPath);
202
+ debug(`Config file found (${stats.size} bytes)`);
203
+ if (resolvedPath.endsWith(".json")) {
204
+ debug("Loading JSON config file...");
205
+ const configFile2 = await readFile(resolvedPath, "utf-8");
206
+ const config2 = JSON.parse(configFile2);
207
+ debug("JSON config loaded successfully", {
208
+ routes: Object.keys(config2.routes || {}),
209
+ hasCors: !!config2.cors,
210
+ hasSsl: !!config2.ssl,
211
+ ports: config2.ports
212
+ });
213
+ return config2;
214
+ }
215
+ if (resolvedPath.endsWith(".ts") || resolvedPath.endsWith(".js")) {
216
+ const fileType = resolvedPath.endsWith(".ts") ? "TypeScript" : "JavaScript";
217
+ debug(`Loading ${fileType} config file...`);
218
+ const configModule = await import(`file://${resolvedPath}`);
219
+ const config2 = configModule.default || configModule;
220
+ debug(`${fileType} config loaded successfully`, {
221
+ routes: Object.keys(config2.routes || {}),
222
+ hasCors: !!config2.cors,
223
+ hasSsl: !!config2.ssl,
224
+ ports: config2.ports
225
+ });
226
+ return config2;
227
+ }
228
+ debug("Loading config as JSON (no extension match)...");
229
+ const configFile = await readFile(resolvedPath, "utf-8");
230
+ const config = JSON.parse(configFile);
231
+ debug("Config loaded successfully", {
232
+ routes: Object.keys(config.routes || {}),
233
+ hasCors: !!config.cors,
234
+ hasSsl: !!config.ssl,
235
+ ports: config.ports
236
+ });
237
+ return config;
238
+ } catch (error) {
239
+ if (error.code === "ENOENT") {
240
+ debug(`Config file not found: ${resolvedPath}`);
241
+ } else {
242
+ debug(`Error loading config file: ${error.message}`);
243
+ }
244
+ return {};
245
+ }
246
+ }
247
+ function loadDefaults() {
248
+ debug("==========================================");
249
+ debug("Starting configuration loading process...");
250
+ debug("==========================================");
251
+ debug("Step 1: Loading default configuration...");
252
+ const defaults = {
253
+ routes: {},
254
+ cors: {
255
+ origin: "*",
256
+ credentials: true
257
+ }
258
+ };
259
+ debug("Defaults loaded", defaults);
260
+ return defaults;
261
+ }
262
+ function loadAndMergeFileConfig(defaults) {
263
+ debug("Step 2: Loading configuration from file...");
264
+ return getConfigFromFile().then((fileConfig) => {
265
+ if (Object.keys(fileConfig).length > 0) {
266
+ debug("File config loaded", fileConfig);
267
+ } else {
268
+ debug("No file configuration loaded");
269
+ }
270
+ return deepMerge(defaults, fileConfig);
271
+ });
272
+ }
273
+ function mergeEnvConfig(config) {
274
+ debug("Step 3: Loading configuration from environment...");
275
+ const envConfig = getConfigFromEnv();
276
+ return deepMerge(config, envConfig);
277
+ }
278
+ function mergeProgrammaticConfig(config, programmaticConfig) {
279
+ if (programmaticConfig && Object.keys(programmaticConfig).length > 0) {
280
+ debug("Step 4: Programmatic configuration provided", programmaticConfig);
281
+ return deepMerge(config, programmaticConfig);
282
+ }
283
+ return config;
284
+ }
285
+ function logMergedConfig(merged) {
286
+ debug("Step 5: Final merged configuration", {
287
+ routes: Object.keys(merged.routes || {}),
288
+ ports: merged.ports,
289
+ hasCors: !!merged.cors,
290
+ hasSsl: !!merged.ssl,
291
+ hasValidate: !!merged.validate
292
+ });
293
+ return merged;
294
+ }
295
+ function initializeSSLIfConfigured(config) {
296
+ debug("Step 6: Checking SSL configuration...");
297
+ if (!config.ssl) {
298
+ debug("No SSL configuration provided");
299
+ return Promise.resolve({ config, sslConfig: void 0 });
300
+ }
301
+ return initializeSSL(config.ssl).then((sslConfig) => ({ config, sslConfig }));
302
+ }
303
+ function createFinalConfig({ config, sslConfig }) {
304
+ debug("Step 7: Creating ProxyConfig object...");
305
+ const proxyConfig = createProxyConfig(config, sslConfig);
306
+ debug("==========================================");
307
+ debug("Configuration loading completed successfully");
308
+ debug("==========================================");
309
+ return proxyConfig;
310
+ }
311
+ function loadConfig(programmaticConfig) {
312
+ return Promise.resolve(loadDefaults()).then((defaults) => loadAndMergeFileConfig(defaults)).then((config) => mergeEnvConfig(config)).then((config) => mergeProgrammaticConfig(config, programmaticConfig)).then((merged) => logMergedConfig(merged)).then((merged) => initializeSSLIfConfigured(merged)).then((result) => createFinalConfig(result));
313
+ }
314
+
315
+ export { debug as d, loadConfig as l };
@@ -0,0 +1,137 @@
1
+ import { OutgoingHttpHeaders, IncomingMessage } from 'node:http';
2
+ import { IncomingHttpHeaders } from 'node:http2';
3
+ import { TLSSocket } from 'node:tls';
4
+ import WebSocket from 'ws';
5
+
6
+ declare enum RequestType {
7
+ API = "api",
8
+ STREAM = "stream",
9
+ WEBSOCKET = "websocket"
10
+ }
11
+ interface TLSWebSocket extends WebSocket {
12
+ _socket: TLSSocket;
13
+ }
14
+ type Router = (req: any, res: any) => void;
15
+ type WebSocketRouter = (req: IncomingMessage, socket: TLSWebSocket, headers: IncomingHttpHeaders) => void;
16
+ type Streamer = (stream: any, headers: any, flags: any) => void;
17
+ interface ForwardValidationResult {
18
+ status: boolean;
19
+ message?: string;
20
+ code?: number;
21
+ headers?: OutgoingHttpHeaders;
22
+ }
23
+ interface ConnectionInfo {
24
+ id: number;
25
+ method: string;
26
+ path: string;
27
+ remoteAddress: string;
28
+ scheme: string;
29
+ authority: string;
30
+ origin: string;
31
+ headers: IncomingHttpHeaders;
32
+ query: URLSearchParams;
33
+ type: RequestType;
34
+ respond: (status: number, headers?: Record<string, string>, body?: string) => void;
35
+ end: (body?: string) => void;
36
+ }
37
+
38
+ /**
39
+ * Route configuration for a specific domain/host
40
+ */
41
+ interface RouteConfig {
42
+ /** Target host:port to proxy to */
43
+ target: string;
44
+ /** Enable SSL/TLS for the target */
45
+ ssl?: boolean;
46
+ /** Remap the URL path before forwarding */
47
+ remap?: (url: string) => string;
48
+ /** Custom CORS configuration */
49
+ cors?: CorsConfig;
50
+ /** Validation function for this route */
51
+ validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
52
+ }
53
+ /**
54
+ * CORS configuration
55
+ */
56
+ interface CorsConfig {
57
+ /** Allowed origins (array of strings or '*' for all) */
58
+ origin?: string | string[];
59
+ /** Allowed HTTP methods */
60
+ methods?: string[];
61
+ /** Allowed headers */
62
+ allowedHeaders?: string[];
63
+ /** Exposed headers */
64
+ exposedHeaders?: string[];
65
+ /** Allow credentials */
66
+ credentials?: boolean;
67
+ /** Max age for preflight cache */
68
+ maxAge?: number;
69
+ }
70
+ /**
71
+ * SSL/TLS certificate configuration
72
+ */
73
+ interface CertificateConfig {
74
+ /** Path to private key file or key content */
75
+ key: string;
76
+ /** Path to certificate file or cert content */
77
+ cert: string;
78
+ /** Path to CA file or CA content */
79
+ ca?: string;
80
+ /** Allow HTTP/1.1 fallback */
81
+ allowHTTP1?: boolean;
82
+ }
83
+ /**
84
+ * Server configuration
85
+ */
86
+ interface ServerConfig {
87
+ /** SSL/TLS certificate configuration */
88
+ ssl?: CertificateConfig;
89
+ /** Route configurations mapped by host */
90
+ routes: Record<string, RouteConfig>;
91
+ /** Default CORS configuration */
92
+ cors?: CorsConfig;
93
+ /** Global validation function */
94
+ validate?: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
95
+ /** Ports to listen on (defaults to [443, 8080, 9229]) */
96
+ ports?: number[];
97
+ }
98
+ /**
99
+ * Processed SSL configuration with loaded certificates
100
+ */
101
+ interface LoadedSSLConfig {
102
+ key: Buffer;
103
+ cert: Buffer;
104
+ ca?: Buffer;
105
+ allowHTTP1: boolean;
106
+ }
107
+ /**
108
+ * Proxy configuration object with loaded SSL
109
+ */
110
+ interface ProxyConfig {
111
+ config: ServerConfig;
112
+ sslConfig?: LoadedSSLConfig;
113
+ getSSL: () => LoadedSSLConfig | undefined;
114
+ getRoute: (host: string) => RouteConfig | undefined;
115
+ getTarget: (host: string) => {
116
+ target: string | undefined;
117
+ ssl: LoadedSSLConfig | undefined;
118
+ remap: ((url: string) => string) | undefined;
119
+ };
120
+ getCorsHeaders: (origin: string, host?: string) => OutgoingHttpHeaders;
121
+ validate: (info: ConnectionInfo) => Promise<ForwardValidationResult>;
122
+ getPorts: () => number[];
123
+ getConfig: () => ServerConfig;
124
+ }
125
+ /**
126
+ * Load configuration with cascading priority using promise chain:
127
+ * 1. Programmatic config (highest priority)
128
+ * 2. Environment variables
129
+ * 3. Config file
130
+ * 4. Defaults (lowest priority)
131
+ *
132
+ * Supports config file types: .json, .ts, .js
133
+ */
134
+ declare function loadConfig(programmaticConfig?: Partial<ServerConfig>): Promise<ProxyConfig>;
135
+
136
+ export { RequestType as e, loadConfig as l };
137
+ export type { CorsConfig as C, ForwardValidationResult as F, LoadedSSLConfig as L, ProxyConfig as P, Router as R, ServerConfig as S, TLSWebSocket as T, WebSocketRouter as W, Streamer as a, RouteConfig as b, CertificateConfig as c, ConnectionInfo as d };
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { P as ProxyConfig, T as TLSWebSocket, S as ServerConfig, R as Router, a as Streamer, W as WebSocketRouter } from './config-CCOdQL7F.js';
2
- export { c as CertificateConfig, d as ConnectionInfo, C as CorsConfig, F as ForwardValidationResult, e as RequestType, b as RouteConfig, l as loadConfig } from './config-CCOdQL7F.js';
1
+ import { P as ProxyConfig, T as TLSWebSocket, S as ServerConfig, R as Router, a as Streamer, W as WebSocketRouter } from './config-DtfpOtWI.js';
2
+ export { c as CertificateConfig, d as ConnectionInfo, C as CorsConfig, F as ForwardValidationResult, e as RequestType, b as RouteConfig, l as loadConfig } from './config-DtfpOtWI.js';
3
3
  import { IncomingMessage, ServerResponse, IncomingHttpHeaders as IncomingHttpHeaders$1 } from 'node:http';
4
4
  import { ServerHttp2Stream, IncomingHttpHeaders } from 'node:http2';
5
5
  import 'node:tls';
@@ -33,11 +33,22 @@ declare class ProxyServer {
33
33
  }
34
34
  /**
35
35
  * Create and start a proxy server
36
- * Supports all config file types: .json, .cjs, .ts, .js, .mjs
36
+ * Supports config file types: .json, .ts, .js
37
+ *
38
+ * Promise chain:
39
+ * 1. Load configuration from file/env/programmatic
40
+ * 2. Initialize SSL certificates
41
+ * 3. Create proxy server instance
42
+ * 4. Start listening on configured ports
37
43
  */
38
44
  declare function createProxy(config?: Partial<ServerConfig>): Promise<ProxyServer>;
39
45
  /**
40
46
  * Create a proxy server with custom handlers
47
+ *
48
+ * Promise chain:
49
+ * 1. Load configuration from file/env/programmatic
50
+ * 2. Initialize SSL certificates
51
+ * 3. Create servers with custom handlers
41
52
  */
42
53
  declare function createCustomProxy(config: Partial<ServerConfig> | undefined, handlers: {
43
54
  rest?: Router;
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import { createServer as createServer$1, request } from 'node:https';
5
5
  import { constants, createSecureServer, createServer, connect } from 'node:http2';
6
6
  import { exec } from 'node:child_process';
7
7
  import { WebSocketServer, WebSocket } from 'ws';
8
- import { loadConfig } from './utils/config.js';
9
- import 'node:fs';
8
+ import { d as debug, l as loadConfig } from './config-C-0wrirG.js';
9
+ import 'node:fs/promises';
10
10
  import 'node:path';
11
11
 
12
12
  const num = (seed) => {
@@ -224,7 +224,6 @@ function isMediaFile(path) {
224
224
  });
225
225
  }
226
226
 
227
- const DEBUG$2 = process.env.DEBUG === "true";
228
227
  const restAPIProxyHandler = async (req, res, config) => {
229
228
  const { target, ssl, remap } = config.getTarget(req.headers.host || req.headers[":authority"]?.toString() || "");
230
229
  if (req.httpVersion === "2.0" && ssl) return;
@@ -233,12 +232,12 @@ const restAPIProxyHandler = async (req, res, config) => {
233
232
  res.end("Not Found");
234
233
  return;
235
234
  }
236
- if (DEBUG$2) console.log("HTTP1 rest proxy", `${ssl ? "https" : "http"}://${target}${req.url}`, req.headers.host);
235
+ debug(`HTTP1 rest proxy for ${req.headers.host}`, `${ssl ? "https" : "http"}://${target}${req.url}`);
237
236
  if (remap) req.url = remap(req.url || "");
238
237
  const requestFn = ssl ? request : request$1;
239
238
  const headers = req.httpVersion === "2.0" ? http2HeadersToHttp1Headers(req.headers) : req.headers;
240
239
  const method = req.httpVersion === "2.0" ? req.headers[":method"]?.toString() : req.method;
241
- if (DEBUG$2) console.log("Proxy Request::", req.url, method, headers);
240
+ debug(`Proxy Request :: ${req.url} ${method} ::`, headers);
242
241
  const proxy = requestFn(
243
242
  `${ssl ? "https" : "http"}://${target}${req.url || ""}`,
244
243
  {
@@ -263,13 +262,13 @@ const restAPIProxyHandler = async (req, res, config) => {
263
262
  }
264
263
  });
265
264
  proxyRes.on("error", (error) => {
266
- if (DEBUG$2) console.error("Proxy response error:", error);
265
+ debug("Proxy response error:", error);
267
266
  if (!res.destroyed) res.destroy(error);
268
267
  });
269
268
  }
270
269
  );
271
270
  proxy.on("error", (error) => {
272
- if (DEBUG$2) console.error("Proxy request error:", error);
271
+ debug("Proxy request error:", error);
273
272
  if (!res.headersSent) {
274
273
  res.writeHead(502, { "Content-Type": "text/plain" });
275
274
  res.end("Bad Gateway");
@@ -286,12 +285,11 @@ const restAPIProxyHandler = async (req, res, config) => {
286
285
  }
287
286
  });
288
287
  req.on("error", (error) => {
289
- if (DEBUG$2) console.error("Client request error:", error);
288
+ debug("Client request error:", error);
290
289
  if (!proxy.destroyed) proxy.destroy(error);
291
290
  });
292
291
  };
293
292
 
294
- const DEBUG$1 = process.env.DEBUG === "true";
295
293
  const streamAPIProxyHandler = async (stream, headers, config) => {
296
294
  const { target, ssl, remap } = config.getTarget(headers[":authority"] || "");
297
295
  if (!ssl) return;
@@ -299,14 +297,9 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
299
297
  stream.destroy(new Error("Not Found"));
300
298
  return;
301
299
  }
302
- if (DEBUG$1)
303
- console.log(
304
- "HTTP2 stream proxy",
305
- `${ssl ? "https" : "http"}://${target}${headers[":path"]}`,
306
- headers[":authority"]
307
- );
300
+ debug(`HTTP2 stream proxy for ${headers[":authority"]}`, `${ssl ? "https" : "http"}://${target}${headers[":path"]}`);
308
301
  if (remap) headers[":path"] = remap(headers[":path"] || "");
309
- if (DEBUG$1) console.log("Proxy Request::", headers[":path"]);
302
+ debug("Proxy Request::", headers[":path"]);
310
303
  const proxy = connect(`https://${target}${headers[":path"]}`, {
311
304
  ...ssl,
312
305
  rejectUnauthorized: false
@@ -315,7 +308,7 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
315
308
  const request = proxy.request(headers);
316
309
  request.on("response", (headerResponse) => {
317
310
  if (!stream.writableEnded && !stream.closed && !stream.destroyed) {
318
- if (DEBUG$1) console.log("Proxy Response::", headerResponse[":status"], `for ${headers[":path"]}`);
311
+ debug(`Proxy Response for ${headers[":path"]}::`, headerResponse[":status"]);
319
312
  if (headers[":path"] && isMediaFile(headers[":path"])) {
320
313
  headerResponse["cache-control"] = `public, max-age=${day()}`;
321
314
  }
@@ -342,7 +335,7 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
342
335
  if (!stream.closed && !stream.destroyed) stream.close();
343
336
  });
344
337
  stream.on("error", (error) => {
345
- if (DEBUG$1) console.error("HTTP2 stream proxy error:", error);
338
+ debug("HTTP2 stream proxy error:", error);
346
339
  if (!request.destroyed) request.destroy(error);
347
340
  if (!proxy.closed) proxy.close();
348
341
  });
@@ -360,28 +353,27 @@ const streamAPIProxyHandler = async (stream, headers, config) => {
360
353
  if (!stream.closed && !stream.destroyed) stream.close();
361
354
  });
362
355
  request.on("error", (error) => {
363
- if (DEBUG$1) console.error("HTTP2 request proxy error:", error);
356
+ debug("HTTP2 request proxy error:", error);
364
357
  if (!stream.destroyed) stream.destroy(error);
365
358
  return !proxy.closed && proxy.close();
366
359
  });
367
360
  proxy.on("timeout", () => {
368
- if (DEBUG$1) console.error("HTTP/2 client timeout");
361
+ debug("HTTP/2 client timeout");
369
362
  if (!stream.destroyed) stream.destroy(new Error("HTTP/2 client timeout"));
370
363
  });
371
364
  });
372
365
  proxy.on("error", (error) => {
373
- if (DEBUG$1) console.error("HTTP2 proxy connection error:", error);
366
+ debug("HTTP2 proxy connection error:", error);
374
367
  if (!stream.destroyed) {
375
368
  stream.destroy(error);
376
369
  }
377
370
  });
378
371
  };
379
372
 
380
- const DEBUG = process.env.DEBUG === "true";
381
373
  const websocketAPIProxyHandler = async (req, socket, headers, config) => {
382
374
  const { target, ssl, remap } = config.getTarget(req.headers.host || "");
383
375
  if (!target) return socket.close();
384
- if (DEBUG) console.log("HTTP2 websocket proxy", `${ssl ? "https" : "http"}://${target}${req.url}`, headers.host);
376
+ debug(`HTTP2 websocket proxy for ${headers.host}`, `${ssl ? "https" : "http"}://${target}${req.url}`);
385
377
  if (remap) req.url = remap(req.url || "");
386
378
  const proxy = new WebSocket(
387
379
  `${ssl ? "wss" : "ws"}://${target}${req.url || ""}`,
@@ -405,7 +397,7 @@ const websocketAPIProxyHandler = async (req, socket, headers, config) => {
405
397
  proxy.on("close", () => socket.close());
406
398
  socket.on("close", () => proxy.close());
407
399
  proxy.on("error", (error) => {
408
- if (DEBUG) console.error("WebSocket proxy error:", error);
400
+ debug("WebSocket proxy error:", error);
409
401
  socket.close();
410
402
  });
411
403
  };
@@ -486,58 +478,59 @@ class ProxyServer {
486
478
  }
487
479
  }
488
480
  async function createProxy(config) {
489
- const proxyConfig = await loadConfig(config);
490
- const proxy = new ProxyServer(proxyConfig);
491
- await proxy.start();
492
- return proxy;
481
+ return loadConfig(config).then((proxyConfig) => {
482
+ const proxy = new ProxyServer(proxyConfig);
483
+ return proxy.start().then(() => proxy);
484
+ });
493
485
  }
494
486
  async function createCustomProxy(config, handlers) {
495
- const servers = [];
496
487
  console.log("STARTING CUSTOM PROXY SERVER....");
497
- const proxyConfig = await loadConfig(config);
498
- const ssl = proxyConfig.getSSL();
499
- const ports = proxyConfig.getPorts();
500
- const boundRestHandler = (req, res) => {
501
- if (req.url === "/robiki-proxy/health" && req.method === "GET") {
502
- res.writeHead(200, { "Content-Type": "text/plain" });
503
- res.end("OK");
504
- return;
505
- }
506
- return restAPIProxyHandler(req, res, proxyConfig);
507
- };
508
- const boundStreamHandler = (stream, headers) => streamAPIProxyHandler(stream, headers, proxyConfig);
509
- const boundWebsocketHandler = (req, socket, headers) => websocketAPIProxyHandler(req, socket, headers, proxyConfig);
510
- for (const port of ports) {
511
- let server;
512
- if (ssl) {
513
- server = http2(handlers.rest || boundRestHandler, handlers.stream || boundStreamHandler, {
514
- ...ssl,
515
- port
516
- });
517
- } else {
518
- server = http(handlers.rest || boundRestHandler, { port });
519
- }
520
- websocket(server, handlers.websocket || boundWebsocketHandler, (info) => proxyConfig.validate(info));
521
- servers.push(server);
522
- }
523
- console.log("Custom proxy server started successfully");
524
- return {
525
- getConfig: () => proxyConfig,
526
- start: async () => {
527
- },
528
- stop: async () => {
529
- await Promise.all(
530
- servers.map(
531
- (server) => new Promise((resolve, reject) => {
532
- server.close((err) => {
533
- if (err) reject(err);
534
- else resolve();
535
- });
536
- })
537
- )
538
- );
488
+ return loadConfig(config).then((proxyConfig) => {
489
+ const servers = [];
490
+ const ssl = proxyConfig.getSSL();
491
+ const ports = proxyConfig.getPorts();
492
+ const boundRestHandler = (req, res) => {
493
+ if (req.url === "/robiki-proxy/health" && req.method === "GET") {
494
+ res.writeHead(200, { "Content-Type": "text/plain" });
495
+ res.end("OK");
496
+ return;
497
+ }
498
+ return restAPIProxyHandler(req, res, proxyConfig);
499
+ };
500
+ const boundStreamHandler = (stream, headers) => streamAPIProxyHandler(stream, headers, proxyConfig);
501
+ const boundWebsocketHandler = (req, socket, headers) => websocketAPIProxyHandler(req, socket, headers, proxyConfig);
502
+ for (const port of ports) {
503
+ let server;
504
+ if (ssl) {
505
+ server = http2(handlers.rest || boundRestHandler, handlers.stream || boundStreamHandler, {
506
+ ...ssl,
507
+ port
508
+ });
509
+ } else {
510
+ server = http(handlers.rest || boundRestHandler, { port });
511
+ }
512
+ websocket(server, handlers.websocket || boundWebsocketHandler, (info) => proxyConfig.validate(info));
513
+ servers.push(server);
539
514
  }
540
- };
515
+ console.log("Custom proxy server started successfully");
516
+ return {
517
+ getConfig: () => proxyConfig,
518
+ start: async () => {
519
+ },
520
+ stop: async () => {
521
+ await Promise.all(
522
+ servers.map(
523
+ (server) => new Promise((resolve, reject) => {
524
+ server.close((err) => {
525
+ if (err) reject(err);
526
+ else resolve();
527
+ });
528
+ })
529
+ )
530
+ );
531
+ }
532
+ };
533
+ });
541
534
  }
542
535
  if (import.meta.url === `file://${process.argv[1]}`) {
543
536
  const setupErrorHandlers = () => {
@@ -1,5 +1,5 @@
1
1
  import 'node:http';
2
- export { c as CertificateConfig, C as CorsConfig, P as ProxyConfig, b as RouteConfig, S as ServerConfig, l as loadConfig } from '../config-CCOdQL7F.js';
2
+ export { c as CertificateConfig, C as CorsConfig, L as LoadedSSLConfig, P as ProxyConfig, b as RouteConfig, S as ServerConfig, l as loadConfig } from '../config-DtfpOtWI.js';
3
3
  import 'node:http2';
4
4
  import 'node:tls';
5
5
  import 'ws';
@@ -1,232 +1,3 @@
1
- import{createRequire as _pkgrollCR}from"node:module";const require=_pkgrollCR(import.meta.url);import { readFileSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
-
4
- class ProxyConfig {
5
- config;
6
- sslConfig;
7
- constructor(config) {
8
- this.config = config;
9
- this.initializeSSL();
10
- }
11
- /**
12
- * Initialize SSL configuration
13
- */
14
- initializeSSL() {
15
- if (!this.config.ssl) return;
16
- try {
17
- const key = this.config.ssl.key.includes("-----BEGIN") ? Buffer.from(this.config.ssl.key) : readFileSync(resolve(this.config.ssl.key));
18
- const cert = this.config.ssl.cert.includes("-----BEGIN") ? Buffer.from(this.config.ssl.cert) : readFileSync(resolve(this.config.ssl.cert));
19
- const ca = this.config.ssl.ca ? this.config.ssl.ca.includes("-----BEGIN") ? Buffer.from(this.config.ssl.ca) : readFileSync(resolve(this.config.ssl.ca)) : void 0;
20
- this.sslConfig = {
21
- key,
22
- cert,
23
- ca,
24
- allowHTTP1: this.config.ssl.allowHTTP1 ?? true
25
- };
26
- } catch (error) {
27
- console.error("Failed to load SSL certificates:", error);
28
- throw error;
29
- }
30
- }
31
- /**
32
- * Get SSL configuration
33
- */
34
- getSSL() {
35
- return this.sslConfig;
36
- }
37
- /**
38
- * Get route configuration for a host
39
- */
40
- getRoute(host) {
41
- if (this.config.routes[host]) {
42
- return this.config.routes[host];
43
- }
44
- const hostWithoutPort = host.split(":")[0];
45
- if (this.config.routes[hostWithoutPort]) {
46
- return this.config.routes[hostWithoutPort];
47
- }
48
- for (const [pattern, route] of Object.entries(this.config.routes)) {
49
- if (pattern.includes("*")) {
50
- const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$");
51
- if (regex.test(host) || regex.test(hostWithoutPort)) {
52
- return route;
53
- }
54
- }
55
- }
56
- return void 0;
57
- }
58
- /**
59
- * Get target for a host
60
- */
61
- getTarget(host) {
62
- const route = this.getRoute(host);
63
- if (!route) {
64
- return { target: void 0, ssl: void 0, remap: void 0 };
65
- }
66
- return {
67
- target: route.target,
68
- ssl: route.ssl ? this.sslConfig : void 0,
69
- remap: route.remap
70
- };
71
- }
72
- /**
73
- * Get CORS headers for a request
74
- */
75
- getCorsHeaders(origin, host) {
76
- const route = host ? this.getRoute(host) : void 0;
77
- const corsConfig = route?.cors || this.config.cors;
78
- if (!corsConfig) {
79
- return {
80
- "access-control-allow-origin": origin,
81
- "access-control-allow-methods": "*",
82
- "access-control-allow-headers": "*",
83
- "access-control-allow-credentials": "true"
84
- };
85
- }
86
- const headers = {};
87
- if (corsConfig.origin === "*") {
88
- headers["access-control-allow-origin"] = "*";
89
- } else if (Array.isArray(corsConfig.origin)) {
90
- if (corsConfig.origin.includes(origin)) {
91
- headers["access-control-allow-origin"] = origin;
92
- }
93
- } else if (corsConfig.origin) {
94
- headers["access-control-allow-origin"] = corsConfig.origin;
95
- } else {
96
- headers["access-control-allow-origin"] = origin;
97
- }
98
- if (corsConfig.methods) {
99
- headers["access-control-allow-methods"] = corsConfig.methods.join(", ");
100
- } else {
101
- headers["access-control-allow-methods"] = "*";
102
- }
103
- if (corsConfig.allowedHeaders) {
104
- headers["access-control-allow-headers"] = corsConfig.allowedHeaders.join(", ");
105
- } else {
106
- headers["access-control-allow-headers"] = "*";
107
- }
108
- if (corsConfig.exposedHeaders) {
109
- headers["access-control-expose-headers"] = corsConfig.exposedHeaders.join(", ");
110
- }
111
- if (corsConfig.credentials !== void 0) {
112
- headers["access-control-allow-credentials"] = corsConfig.credentials ? "true" : "false";
113
- } else {
114
- headers["access-control-allow-credentials"] = "true";
115
- }
116
- if (corsConfig.maxAge) {
117
- headers["access-control-max-age"] = corsConfig.maxAge.toString();
118
- }
119
- return headers;
120
- }
121
- /**
122
- * Validate a request
123
- */
124
- async validate(info) {
125
- const route = this.getRoute(info.authority);
126
- if (route?.validate) return route.validate(info);
127
- if (this.config.validate) return this.config.validate(info);
128
- return { status: true };
129
- }
130
- /**
131
- * Get ports to listen on
132
- */
133
- getPorts() {
134
- return this.config.ports || [443, 8080, 9229];
135
- }
136
- /**
137
- * Get the full configuration
138
- */
139
- getConfig() {
140
- return this.config;
141
- }
142
- }
143
- function deepMerge(...objects) {
144
- const result = {};
145
- for (const obj of objects) {
146
- if (!obj) continue;
147
- for (const key in obj) {
148
- if (obj[key] === void 0) continue;
149
- if (typeof obj[key] === "object" && !Array.isArray(obj[key]) && obj[key] !== null) {
150
- result[key] = deepMerge(result[key] || {}, obj[key]);
151
- } else {
152
- result[key] = obj[key];
153
- }
154
- }
155
- }
156
- return result;
157
- }
158
- function getConfigFromEnv() {
159
- const config = {};
160
- if (process.env.SSL_KEY && process.env.SSL_CERT) {
161
- config.ssl = {
162
- key: process.env.SSL_KEY,
163
- cert: process.env.SSL_CERT,
164
- ca: process.env.SSL_CA,
165
- allowHTTP1: process.env.SSL_ALLOW_HTTP1 === "true"
166
- };
167
- }
168
- if (process.env.CORS_ORIGIN) {
169
- config.cors = {
170
- origin: process.env.CORS_ORIGIN === "*" ? "*" : process.env.CORS_ORIGIN.split(","),
171
- methods: process.env.CORS_METHODS?.split(","),
172
- allowedHeaders: process.env.CORS_HEADERS?.split(","),
173
- credentials: process.env.CORS_CREDENTIALS === "true"
174
- };
175
- }
176
- return config;
177
- }
178
- function loadTypeScriptConfig(resolvedPath) {
179
- try {
180
- const { register } = require("tsx/cjs/api");
181
- const unregister = register();
182
- try {
183
- delete require.cache[resolvedPath];
184
- const configModule = require(resolvedPath);
185
- return configModule.default || configModule;
186
- } finally {
187
- unregister();
188
- }
189
- } catch (error) {
190
- console.warn("Failed to load TypeScript config file:", error);
191
- console.warn("Make sure tsx is installed: npm install tsx or yarn add tsx");
192
- return {};
193
- }
194
- }
195
- async function getConfigFromFile() {
196
- const configPath = process.env.PROXY_CONFIG || "./proxy.config.json";
197
- try {
198
- const resolvedPath = resolve(configPath);
199
- if (resolvedPath.endsWith(".ts")) {
200
- return loadTypeScriptConfig(resolvedPath);
201
- }
202
- if (resolvedPath.endsWith(".js") || resolvedPath.endsWith(".mjs")) {
203
- const fileUrl = `file://${resolvedPath}`;
204
- const configModule = await import(fileUrl);
205
- return configModule.default || configModule;
206
- }
207
- if (resolvedPath.endsWith(".cjs")) {
208
- delete require.cache[resolvedPath];
209
- const configModule = require(resolvedPath);
210
- return configModule.default || configModule;
211
- }
212
- const configFile = readFileSync(resolvedPath, "utf-8");
213
- return JSON.parse(configFile);
214
- } catch (error) {
215
- return {};
216
- }
217
- }
218
- async function loadConfig(programmaticConfig) {
219
- const defaults = {
220
- routes: {},
221
- cors: {
222
- origin: "*",
223
- credentials: true
224
- }
225
- };
226
- const fileConfig = await getConfigFromFile();
227
- const envConfig = getConfigFromEnv();
228
- const merged = deepMerge(defaults, fileConfig, envConfig, programmaticConfig || {});
229
- return new ProxyConfig(merged);
230
- }
231
-
232
- export { ProxyConfig, loadConfig };
1
+ import 'node:fs/promises';
2
+ import 'node:path';
3
+ export { l as loadConfig } from '../config-C-0wrirG.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robiki/proxy",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A flexible HTTP/2 proxy server with WebSocket support, configurable routing, CORS, and validation. Use as npm package or Docker container.",
5
5
  "keywords": [
6
6
  "proxy",