ninja-reverse-proxy 1.0.0

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 (47) hide show
  1. package/README.md +480 -0
  2. package/config.example.yaml +99 -0
  3. package/dist/Serviceregistry.d.ts +31 -0
  4. package/dist/Serviceregistry.d.ts.map +1 -0
  5. package/dist/Serviceregistry.js +95 -0
  6. package/dist/Serviceregistry.js.map +1 -0
  7. package/dist/auto-scaler.d.ts +27 -0
  8. package/dist/auto-scaler.d.ts.map +1 -0
  9. package/dist/auto-scaler.js +133 -0
  10. package/dist/auto-scaler.js.map +1 -0
  11. package/dist/cache.d.ts +22 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +112 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/config-schema.d.ts +52 -0
  16. package/dist/config-schema.d.ts.map +1 -0
  17. package/dist/config-schema.js +77 -0
  18. package/dist/config-schema.js.map +1 -0
  19. package/dist/config.d.ts +46 -0
  20. package/dist/config.d.ts.map +1 -0
  21. package/dist/config.js +73 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/health.d.ts +5 -0
  24. package/dist/health.d.ts.map +1 -0
  25. package/dist/health.js +78 -0
  26. package/dist/health.js.map +1 -0
  27. package/dist/index.d.ts +3 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +63 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/loadBalancer.d.ts +31 -0
  32. package/dist/loadBalancer.d.ts.map +1 -0
  33. package/dist/loadBalancer.js +158 -0
  34. package/dist/loadBalancer.js.map +1 -0
  35. package/dist/rate-limiter.d.ts +16 -0
  36. package/dist/rate-limiter.d.ts.map +1 -0
  37. package/dist/rate-limiter.js +59 -0
  38. package/dist/rate-limiter.js.map +1 -0
  39. package/dist/server-schema.d.ts +25 -0
  40. package/dist/server-schema.d.ts.map +1 -0
  41. package/dist/server-schema.js +18 -0
  42. package/dist/server-schema.js.map +1 -0
  43. package/dist/server.d.ts +10 -0
  44. package/dist/server.d.ts.map +1 -0
  45. package/dist/server.js +518 -0
  46. package/dist/server.js.map +1 -0
  47. package/package.json +39 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Serviceregistry.js","sourceRoot":"","sources":["../src/Serviceregistry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { LoadBalancer } from "./loadBalancer.js";
2
+ export interface AutoScalerConfig {
3
+ minServers: number;
4
+ maxServers: number;
5
+ scaleUpAt: number;
6
+ scaleDownAt: number;
7
+ cooldownMs: number;
8
+ startPort: number;
9
+ proxyPort: number;
10
+ }
11
+ export declare class AutoScaler {
12
+ private config;
13
+ private lb;
14
+ private spawnedServers;
15
+ private lastScaleAction;
16
+ private nextPort;
17
+ private monitorInterval;
18
+ constructor(config: AutoScalerConfig, lb: LoadBalancer);
19
+ start(): void;
20
+ stop(): void;
21
+ private monitor;
22
+ private spawnServer;
23
+ private killOldestServer;
24
+ private killServer;
25
+ getStats(): object;
26
+ }
27
+ //# sourceMappingURL=auto-scaler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-scaler.d.ts","sourceRoot":"","sources":["../src/auto-scaler.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AASD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAmB;IAEjC,OAAO,CAAC,EAAE,CAAe;IACzB,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,eAAe,CAA+B;gBAE1C,MAAM,EAAE,gBAAgB,EAAE,EAAE,EAAE,YAAY;IAKtD,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,IAAI;IAUZ,OAAO,CAAC,OAAO;IAiDf,OAAO,CAAC,WAAW;IAsCnB,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,UAAU;IASlB,QAAQ,IAAI,MAAM;CAgBnB"}
@@ -0,0 +1,133 @@
1
+ import { spawn, ChildProcess } from "child_process";
2
+ import { join } from "path";
3
+ import { registry } from "./Serviceregistry.js";
4
+ export class AutoScaler {
5
+ config;
6
+ // loadBalancer to get activeConnections with that servers
7
+ lb;
8
+ spawnedServers = new Map();
9
+ lastScaleAction = 0;
10
+ nextPort;
11
+ monitorInterval = null;
12
+ constructor(config, lb) {
13
+ this.config = config;
14
+ this.lb = lb;
15
+ this.nextPort = config.startPort;
16
+ }
17
+ start() {
18
+ console.log(`[AutoScaler] Started — min:${this.config.minServers} max:${this.config.maxServers}`);
19
+ this.monitorInterval = setInterval(() => {
20
+ this.monitor();
21
+ }, 5000);
22
+ }
23
+ stop() {
24
+ if (this.monitorInterval) {
25
+ clearInterval(this.monitorInterval);
26
+ }
27
+ for (const [id] of this.spawnedServers) {
28
+ this.killServer(id);
29
+ }
30
+ console.log(`[AutoScaler] Stopped`);
31
+ }
32
+ monitor() {
33
+ const stats = this.lb.getStats();
34
+ const now = Date.now();
35
+ // to get total Active Connections :
36
+ const totalConnections = Object.values(stats).reduce((sum, s) => {
37
+ return sum + (s.activeConnections ?? 0);
38
+ }, 0);
39
+ console.log(`[AutoScaler] totalConnections: ${totalConnections}`);
40
+ const healthyCount = registry.getHealthy().length;
41
+ // since spawnedServers is an Map so its length will not exist instead of size will be there
42
+ const autoCount = this.spawnedServers.size;
43
+ console.log(`[AutoScaler] Connections: ${totalConnections} | Servers: ${healthyCount} (${autoCount} auto-scaled)`);
44
+ if (now - this.lastScaleAction < this.config.cooldownMs) {
45
+ console.log(`[AutoScaler] Cooldown active — waiting...`);
46
+ return;
47
+ }
48
+ // Scaling Up :
49
+ if (totalConnections > this.config.scaleUpAt &&
50
+ healthyCount < this.config.maxServers) {
51
+ console.log(`[AutoScaler] SCALE UP — connections: ${totalConnections} > threshold: ${this.config.scaleUpAt}`);
52
+ this.spawnServer();
53
+ this.lastScaleAction = now;
54
+ return;
55
+ }
56
+ // scale down
57
+ if (totalConnections < this.config.scaleDownAt &&
58
+ healthyCount > this.config.minServers &&
59
+ autoCount > 0) {
60
+ console.log(`[AutoScaler] SCALE DOWN — connections: ${totalConnections} < threshold: ${this.config.scaleDownAt}`);
61
+ this.killOldestServer();
62
+ this.lastScaleAction = now;
63
+ return;
64
+ }
65
+ }
66
+ spawnServer() {
67
+ const port = this.nextPort++;
68
+ const id = `auto-node-${port}`;
69
+ console.log(`[AutoScaler] Spawning ${id} on port ${port}...`);
70
+ const child = spawn("node", ["server-template.js"], {
71
+ env: {
72
+ ...process.env,
73
+ SERVER_PORT: String(port),
74
+ SERVER_ID: id,
75
+ PROXY_PORT: String(this.config.proxyPort),
76
+ },
77
+ stdio: "pipe", // capture output
78
+ });
79
+ child.stdout?.on("data", (data) => {
80
+ console.log(`[${id}] ${data.toString().trim()}`);
81
+ });
82
+ child.stderr?.on("data", (data) => {
83
+ console.error(`[${id} ERROR] ${data.toString().trim()}`);
84
+ });
85
+ child.on("exit", (code) => {
86
+ console.log(`[AutoScaler] ${id} exited with code ${code}`);
87
+ this.spawnedServers.delete(id);
88
+ });
89
+ this.spawnedServers.set(id, {
90
+ id,
91
+ port,
92
+ process: child,
93
+ spawnedAt: Date.now(),
94
+ });
95
+ console.log(`[AutoScaler]${id} spawned on port ${port}`);
96
+ }
97
+ killOldestServer() {
98
+ let oldest = null;
99
+ for (const [, server] of this.spawnedServers) {
100
+ if (!oldest || server.spawnedAt < oldest.spawnedAt) {
101
+ oldest = server;
102
+ }
103
+ }
104
+ if (oldest) {
105
+ this.killServer(oldest.id);
106
+ }
107
+ }
108
+ killServer(id) {
109
+ const server = this.spawnedServers.get(id);
110
+ if (!server)
111
+ return;
112
+ console.log(`[AutoScaler] Killing ${id}...`);
113
+ server.process.kill("SIGTERM"); // graceful shutdown
114
+ this.spawnedServers.delete(id);
115
+ }
116
+ getStats() {
117
+ return {
118
+ spawnedServers: this.spawnedServers.size,
119
+ servers: [...this.spawnedServers.values()].map((s) => ({
120
+ id: s.id,
121
+ port: s.port,
122
+ uptime: `${Math.floor((Date.now() - s.spawnedAt) / 1000)}s`,
123
+ })),
124
+ config: {
125
+ minServers: this.config.minServers,
126
+ maxServers: this.config.maxServers,
127
+ scaleUpAt: this.config.scaleUpAt,
128
+ scaleDownAt: this.config.scaleDownAt,
129
+ },
130
+ };
131
+ }
132
+ }
133
+ //# sourceMappingURL=auto-scaler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-scaler.js","sourceRoot":"","sources":["../src/auto-scaler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export interface CacheConfig {
2
+ host: string;
3
+ port: number;
4
+ ttlSeconds: number;
5
+ enabled: boolean;
6
+ }
7
+ export declare class Cache {
8
+ private client;
9
+ private ttlSeconds;
10
+ private enabled;
11
+ private connected;
12
+ constructor(config: CacheConfig);
13
+ connect(): Promise<void>;
14
+ buildKey(method: string, url: string): string;
15
+ get(key: string): Promise<string | null>;
16
+ set(key: string, value: string): Promise<void>;
17
+ invalidate(pattern: string): Promise<void>;
18
+ disconnect(): Promise<void>;
19
+ isConnected(): boolean;
20
+ getStats(): object;
21
+ }
22
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,SAAS,CAAkB;gBAEvB,MAAM,EAAE,WAAW;IAiCzB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIvC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAgBxC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9C,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa1C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC,WAAW,IAAI,OAAO;IAItB,QAAQ,IAAI,MAAM;CAOnB"}
package/dist/cache.js ADDED
@@ -0,0 +1,112 @@
1
+ import { createClient } from "redis";
2
+ export class Cache {
3
+ client;
4
+ ttlSeconds;
5
+ enabled;
6
+ connected = false;
7
+ constructor(config) {
8
+ this.ttlSeconds = config.ttlSeconds;
9
+ this.enabled = config.enabled;
10
+ this.client = createClient({
11
+ socket: {
12
+ host: config.host,
13
+ port: config.port,
14
+ reconnectStrategy: (retries) => {
15
+ if (retries > 5) {
16
+ console.error(`[Cache] Redis reconnect failed after ${retries} attempts`);
17
+ return new Error("Redis reconnect failed");
18
+ }
19
+ return retries * 500;
20
+ },
21
+ },
22
+ });
23
+ this.client.on("error", (err) => {
24
+ console.error(`[Cache] Redis error: ${err.message}`);
25
+ this.connected = false;
26
+ });
27
+ this.client.on("connect", () => {
28
+ console.log(`[Cache] Redis connected at ${config.host}:${config.port}`);
29
+ this.connected = true;
30
+ });
31
+ this.client.on("reconnecting", () => {
32
+ console.log(`[Cache] Redis reconnecting...`);
33
+ });
34
+ }
35
+ async connect() {
36
+ if (!this.enabled) {
37
+ console.log(`[Cache] Caching is disabled — skipping Redis connection`);
38
+ return;
39
+ }
40
+ try {
41
+ await this.client.connect();
42
+ this.connected = true;
43
+ }
44
+ catch (err) {
45
+ console.error(`[Cache] Failed to connect to Redis: ${err.message}`);
46
+ this.connected = false;
47
+ }
48
+ }
49
+ // Build a unique cache key from method + url
50
+ buildKey(method, url) {
51
+ return `proxy:${method}:${url}`;
52
+ }
53
+ async get(key) {
54
+ if (!this.enabled || !this.connected)
55
+ return null;
56
+ try {
57
+ const value = await this.client.get(key);
58
+ if (value) {
59
+ console.log(`[Cache] HIT → ${key}`);
60
+ }
61
+ else {
62
+ console.log(`[Cache] MISS → ${key}`);
63
+ }
64
+ return value;
65
+ }
66
+ catch (err) {
67
+ console.error(`[Cache] GET error: ${err.message}`);
68
+ return null;
69
+ }
70
+ }
71
+ async set(key, value) {
72
+ if (!this.enabled || !this.connected)
73
+ return;
74
+ try {
75
+ await this.client.set(key, value, { EX: this.ttlSeconds });
76
+ console.log(`[Cache] SET → ${key} (TTL: ${this.ttlSeconds}s)`);
77
+ }
78
+ catch (err) {
79
+ console.error(`[Cache] SET error: ${err.message}`);
80
+ }
81
+ }
82
+ async invalidate(pattern) {
83
+ if (!this.enabled || !this.connected)
84
+ return;
85
+ try {
86
+ const keys = await this.client.keys(`proxy:*:${pattern}`);
87
+ if (keys.length > 0) {
88
+ await this.client.del(keys);
89
+ console.log(`[Cache] INVALIDATED ${keys.length} keys for pattern: ${pattern}`);
90
+ }
91
+ }
92
+ catch (err) {
93
+ console.error(`[Cache] INVALIDATE error: ${err.message}`);
94
+ }
95
+ }
96
+ async disconnect() {
97
+ if (this.connected) {
98
+ console.log(`[Cache] Redis disconnected`);
99
+ }
100
+ }
101
+ isConnected() {
102
+ return this.connected;
103
+ }
104
+ getStats() {
105
+ return {
106
+ enabled: this.enabled,
107
+ connected: this.connected,
108
+ ttlSeconds: this.ttlSeconds,
109
+ };
110
+ }
111
+ }
112
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { z } from "zod";
2
+ export declare const rootConfigSchema: z.ZodObject<{
3
+ server: z.ZodObject<{
4
+ listen: z.ZodNumber;
5
+ httpsPort: z.ZodOptional<z.ZodNumber>;
6
+ workers: z.ZodOptional<z.ZodNumber>;
7
+ connectTimeoutMs: z.ZodDefault<z.ZodNumber>;
8
+ readTimeoutMs: z.ZodDefault<z.ZodNumber>;
9
+ compression: z.ZodDefault<z.ZodBoolean>;
10
+ accessLog: z.ZodOptional<z.ZodString>;
11
+ upstreams: z.ZodArray<z.ZodObject<{
12
+ id: z.ZodString;
13
+ url: z.ZodString;
14
+ weight: z.ZodDefault<z.ZodNumber>;
15
+ }, z.core.$strip>>;
16
+ headers: z.ZodOptional<z.ZodArray<z.ZodObject<{
17
+ key: z.ZodString;
18
+ value: z.ZodString;
19
+ }, z.core.$strip>>>;
20
+ paths: z.ZodArray<z.ZodObject<{
21
+ path: z.ZodString;
22
+ upstream: z.ZodArray<z.ZodString>;
23
+ rateLimit: z.ZodOptional<z.ZodObject<{
24
+ windowMs: z.ZodNumber;
25
+ maxRequests: z.ZodNumber;
26
+ }, z.core.$strip>>;
27
+ sticky: z.ZodDefault<z.ZodBoolean>;
28
+ }, z.core.$strip>>;
29
+ loadBalancing: z.ZodDefault<z.ZodObject<{
30
+ strategy: z.ZodDefault<z.ZodEnum<{
31
+ "round-robin": "round-robin";
32
+ "least-connections": "least-connections";
33
+ "ip-hash": "ip-hash";
34
+ random: "random";
35
+ }>>;
36
+ failureThreshold: z.ZodDefault<z.ZodNumber>;
37
+ recoveryTimeMs: z.ZodDefault<z.ZodNumber>;
38
+ retry: z.ZodDefault<z.ZodObject<{
39
+ maxAttempts: z.ZodDefault<z.ZodNumber>;
40
+ statusCodes: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
41
+ }, z.core.$strip>>;
42
+ }, z.core.$strip>>;
43
+ cache: z.ZodDefault<z.ZodObject<{
44
+ enabled: z.ZodDefault<z.ZodBoolean>;
45
+ host: z.ZodDefault<z.ZodString>;
46
+ port: z.ZodDefault<z.ZodNumber>;
47
+ ttlSeconds: z.ZodDefault<z.ZodNumber>;
48
+ }, z.core.$strip>>;
49
+ }, z.core.$strip>;
50
+ }, z.core.$strip>;
51
+ export type ConfigSchemaType = z.infer<typeof rootConfigSchema>;
52
+ //# sourceMappingURL=config-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../src/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiFxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAE3B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
@@ -0,0 +1,77 @@
1
+ import { z } from "zod";
2
+ const upstreamSchema = z.object({
3
+ id: z.string(),
4
+ url: z.string().url(),
5
+ weight: z.number().default(1),
6
+ });
7
+ const headerSchema = z.object({
8
+ key: z.string(),
9
+ value: z.string(),
10
+ });
11
+ const rateLimitSchema = z
12
+ .object({
13
+ windowMs: z.number(),
14
+ maxRequests: z.number(),
15
+ })
16
+ .optional();
17
+ const pathRuleSchema = z.object({
18
+ path: z.string(),
19
+ upstream: z.array(z.string()),
20
+ rateLimit: rateLimitSchema,
21
+ sticky: z.boolean().default(false),
22
+ });
23
+ const loadBalancerSchema = z
24
+ .object({
25
+ strategy: z
26
+ .enum(["round-robin", "least-connections", "ip-hash", "random"])
27
+ .default("least-connections"),
28
+ failureThreshold: z.number().default(3),
29
+ recoveryTimeMs: z.number().default(15000),
30
+ retry: z
31
+ .object({
32
+ maxAttempts: z.number().default(2),
33
+ statusCodes: z.array(z.number()).default([502, 503, 504]),
34
+ })
35
+ .default({
36
+ maxAttempts: 2,
37
+ statusCodes: [502, 503, 504],
38
+ }),
39
+ })
40
+ .default({
41
+ strategy: "least-connections",
42
+ failureThreshold: 3,
43
+ recoveryTimeMs: 15000,
44
+ retry: {
45
+ maxAttempts: 2,
46
+ statusCodes: [502, 503, 504],
47
+ },
48
+ });
49
+ const cacheSchema = z.object({
50
+ enabled: z.boolean().default(false),
51
+ host: z.string().default("redis"),
52
+ port: z.number().default(6379),
53
+ ttlSeconds: z.number().default(60),
54
+ }).default({
55
+ enabled: false,
56
+ host: "redis",
57
+ port: 6379,
58
+ ttlSeconds: 60
59
+ });
60
+ const serverSchema = z.object({
61
+ listen: z.number(),
62
+ httpsPort: z.number().optional(),
63
+ workers: z.number().optional(),
64
+ connectTimeoutMs: z.number().default(5000),
65
+ readTimeoutMs: z.number().default(15000),
66
+ compression: z.boolean().default(false),
67
+ accessLog: z.string().optional(),
68
+ upstreams: z.array(upstreamSchema),
69
+ headers: z.array(headerSchema).optional(),
70
+ paths: z.array(pathRuleSchema),
71
+ loadBalancing: loadBalancerSchema,
72
+ cache: cacheSchema,
73
+ });
74
+ export const rootConfigSchema = z.object({
75
+ server: serverSchema,
76
+ });
77
+ //# sourceMappingURL=config-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.js","sourceRoot":"","sources":["../src/config-schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ export declare function parseYAMLConfig(filepath: string): Promise<any>;
2
+ export declare function validateConfig(config: any): Promise<{
3
+ server: {
4
+ listen: number;
5
+ connectTimeoutMs: number;
6
+ readTimeoutMs: number;
7
+ compression: boolean;
8
+ upstreams: {
9
+ id: string;
10
+ url: string;
11
+ weight: number;
12
+ }[];
13
+ paths: {
14
+ path: string;
15
+ upstream: string[];
16
+ sticky: boolean;
17
+ rateLimit?: {
18
+ windowMs: number;
19
+ maxRequests: number;
20
+ } | undefined;
21
+ }[];
22
+ loadBalancing: {
23
+ strategy: "round-robin" | "least-connections" | "ip-hash" | "random";
24
+ failureThreshold: number;
25
+ recoveryTimeMs: number;
26
+ retry: {
27
+ maxAttempts: number;
28
+ statusCodes: number[];
29
+ };
30
+ };
31
+ cache: {
32
+ enabled: boolean;
33
+ host: string;
34
+ port: number;
35
+ ttlSeconds: number;
36
+ };
37
+ httpsPort?: number | undefined;
38
+ workers?: number | undefined;
39
+ accessLog?: string | undefined;
40
+ headers?: {
41
+ key: string;
42
+ value: string;
43
+ }[] | undefined;
44
+ };
45
+ }>;
46
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA,wBAAsB,eAAe,CAAC,QAAQ,EAAG,MAAM,gBAkEtD;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAE/C"}
package/dist/config.js ADDED
@@ -0,0 +1,73 @@
1
+ import { promises as fs } from 'fs';
2
+ import { parse } from 'yaml';
3
+ import { rootConfigSchema } from "./config-schema.js";
4
+ import path from 'path';
5
+ export async function parseYAMLConfig(filepath) {
6
+ const configFileContent = await fs.readFile(filepath, 'utf-8');
7
+ const configParsed = parse(configFileContent) || {};
8
+ if (!configParsed.server) {
9
+ configParsed.server = {};
10
+ }
11
+ const configDir = path.join(path.dirname(filepath), "config.d");
12
+ let dirExists = false;
13
+ try {
14
+ const stat = await fs.stat(configDir);
15
+ dirExists = stat.isDirectory();
16
+ }
17
+ catch {
18
+ dirExists = false;
19
+ }
20
+ if (dirExists) {
21
+ try {
22
+ const files = await fs.readdir(configDir);
23
+ const yamlFiles = files.filter(f => f.endsWith(".yaml") || f.endsWith(".yml"));
24
+ // Read and parse all files concurrently
25
+ const parsePromises = yamlFiles.map(async (file) => {
26
+ const extraFilePath = path.join(configDir, file);
27
+ try {
28
+ const extraContent = await fs.readFile(extraFilePath, "utf-8");
29
+ return parse(extraContent);
30
+ }
31
+ catch (err) {
32
+ console.error(`[Config] Skipped loading broken file ${file}: ${err.message}`);
33
+ return null; // Return null so we can filter it out safely
34
+ }
35
+ });
36
+ const parsedFilesResults = await Promise.all(parsePromises);
37
+ // Merge results sequentially in memory (very fast)
38
+ for (const extraParsed of parsedFilesResults) {
39
+ if (extraParsed && extraParsed.server) {
40
+ // Check and merge upstreams safely
41
+ if (extraParsed.server.upstreams && Array.isArray(extraParsed.server.upstreams)) {
42
+ configParsed.server.upstreams = [
43
+ ...(configParsed.server.upstreams || []),
44
+ ...extraParsed.server.upstreams
45
+ ];
46
+ }
47
+ // Check and merge paths safely
48
+ if (extraParsed.server.paths && Array.isArray(extraParsed.server.paths)) {
49
+ configParsed.server.paths = [
50
+ ...(configParsed.server.paths || []),
51
+ ...extraParsed.server.paths
52
+ ];
53
+ }
54
+ // Check and merge headers safely
55
+ if (extraParsed.server.headers && Array.isArray(extraParsed.server.headers)) {
56
+ configParsed.server.headers = [
57
+ ...(configParsed.server.headers || []),
58
+ ...extraParsed.server.headers
59
+ ];
60
+ }
61
+ }
62
+ }
63
+ }
64
+ catch (dirErr) {
65
+ console.error(`[Config] Error reading config.d directory: ${dirErr.message}`);
66
+ }
67
+ }
68
+ return configParsed;
69
+ }
70
+ export async function validateConfig(config) {
71
+ return await rootConfigSchema.parseAsync(config);
72
+ }
73
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAiB;IACpD,MAAM,iBAAiB,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAC/D,MAAM,YAAY,GAAG,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,CAAC;IACpD,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,YAAY,CAAC,MAAM,GAAG,EAAE,CAAC;IAC5B,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;IAChE,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QACF,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACtC,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACN,SAAS,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,IAAI,SAAS,EAAE,CAAC;QACb,IAAI,CAAC;YACF,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC1C,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAE/E,wCAAwC;YACxC,MAAM,aAAa,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;gBAChD,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;gBACjD,IAAI,CAAC;oBACF,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;oBAC/D,OAAO,KAAK,CAAC,YAAY,CAAC,CAAC;gBAC9B,CAAC;gBAAC,OAAO,GAAQ,EAAE,CAAC;oBACjB,OAAO,CAAC,KAAK,CAAC,wCAAwC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;oBAC9E,OAAO,IAAI,CAAC,CAAC,6CAA6C;gBAC7D,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YAE5D,mDAAmD;YACnD,KAAK,MAAM,WAAW,IAAI,kBAAkB,EAAE,CAAC;gBAC5C,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;oBACrC,mCAAmC;oBACnC,IAAI,WAAW,CAAC,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;wBAC/E,YAAY,CAAC,MAAM,CAAC,SAAS,GAAG;4BAC7B,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,CAAC;4BACxC,GAAG,WAAW,CAAC,MAAM,CAAC,SAAS;yBACjC,CAAC;oBACL,CAAC;oBACD,+BAA+B;oBAC/B,IAAI,WAAW,CAAC,MAAM,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;wBACvE,YAAY,CAAC,MAAM,CAAC,KAAK,GAAG;4BACzB,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;4BACpC,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK;yBAC7B,CAAC;oBACL,CAAC;oBACD,iCAAiC;oBACjC,IAAI,WAAW,CAAC,MAAM,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC3E,YAAY,CAAC,MAAM,CAAC,OAAO,GAAG;4BAC3B,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;4BACtC,GAAG,WAAW,CAAC,MAAM,CAAC,OAAO;yBAC/B,CAAC;oBACL,CAAC;gBACJ,CAAC;YACJ,CAAC;QACJ,CAAC;QAAC,OAAO,MAAW,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,8CAA8C,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;QACjF,CAAC;IACJ,CAAC;IAED,OAAO,YAAY,CAAE;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAW;IAC9C,OAAO,MAAM,gBAAgB,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { ConfigSchemaType } from "./config-schema.js";
2
+ import { LoadBalancer } from "./loadBalancer.js";
3
+ export declare function initialHealthCheck(upstreams: ConfigSchemaType["server"]["upstreams"], HEALTHY_UPSTREAMS: Set<string>): Promise<void>;
4
+ export declare function startHealthChecks(upstreams: ConfigSchemaType["server"]["upstreams"], HEALTHY_UPSTREAMS: Set<string>, lb: LoadBalancer): Promise<void>;
5
+ //# sourceMappingURL=health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../src/health.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGjD,wBAAsB,kBAAkB,CACtC,SAAS,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,EAClD,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,iBAmC/B;AAGD,wBAAsB,iBAAiB,CACrC,SAAS,EAAE,gBAAgB,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,EAClD,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,EAC9B,EAAE,EAAE,YAAY,iBAwCjB"}