huge refactor to remove depenencie hell and add typesafe container
This commit is contained in:
parent
28b9822d55
commit
843a7b9b9b
148 changed files with 3603 additions and 2378 deletions
|
|
@ -1,9 +1,9 @@
|
|||
import { join } from 'path';
|
||||
import { z } from 'zod';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import { EnvLoader } from './loaders/env.loader';
|
||||
import { FileLoader } from './loaders/file.loader';
|
||||
import { ConfigError, ConfigValidationError } from './errors';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import type {
|
||||
ConfigLoader,
|
||||
ConfigManagerOptions,
|
||||
|
|
@ -82,9 +82,9 @@ export class ConfigManager<T = Record<string, unknown>> {
|
|||
expected: (err as any).expected,
|
||||
received: (err as any).received,
|
||||
}));
|
||||
|
||||
|
||||
this.logger.error('Configuration validation failed:', errorDetails);
|
||||
|
||||
|
||||
throw new ConfigValidationError('Configuration validation failed', error.errors);
|
||||
}
|
||||
throw error;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Import necessary types
|
||||
import { z } from 'zod';
|
||||
import { EnvLoader } from './loaders/env.loader';
|
||||
import { FileLoader } from './loaders/file.loader';
|
||||
import { ConfigManager } from './config-manager';
|
||||
import type { BaseAppConfig } from './schemas';
|
||||
import { baseAppSchema } from './schemas';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Legacy singleton instance for backward compatibility
|
||||
let configInstance: ConfigManager<BaseAppConfig> | null = null;
|
||||
|
|
@ -56,7 +56,6 @@ function loadCriticalEnvVarsSync(): void {
|
|||
// Load critical env vars immediately
|
||||
loadCriticalEnvVarsSync();
|
||||
|
||||
|
||||
/**
|
||||
* Initialize configuration for a service in a monorepo.
|
||||
* Automatically loads configs from:
|
||||
|
|
@ -121,8 +120,6 @@ export function getLogConfig() {
|
|||
return getConfig().log;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function getQueueConfig() {
|
||||
return getConfig().queue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from 'bun:test';
|
||||
import { unifiedAppSchema, toUnifiedConfig, getStandardServiceName } from '../unified-app.schema';
|
||||
import { getStandardServiceName, toUnifiedConfig, unifiedAppSchema } from '../unified-app.schema';
|
||||
|
||||
describe('UnifiedAppConfig', () => {
|
||||
describe('getStandardServiceName', () => {
|
||||
|
|
@ -74,13 +74,13 @@ describe('UnifiedAppConfig', () => {
|
|||
};
|
||||
|
||||
const result = unifiedAppSchema.parse(config);
|
||||
|
||||
|
||||
// Should have both nested and flat structure
|
||||
expect(result.postgres).toBeDefined();
|
||||
expect(result.mongodb).toBeDefined();
|
||||
expect(result.database?.postgres).toBeDefined();
|
||||
expect(result.database?.mongodb).toBeDefined();
|
||||
|
||||
|
||||
// Values should match
|
||||
expect(result.postgres?.host).toBe('localhost');
|
||||
expect(result.postgres?.port).toBe(5432);
|
||||
|
|
@ -144,7 +144,7 @@ describe('UnifiedAppConfig', () => {
|
|||
};
|
||||
|
||||
const unified = toUnifiedConfig(stockBotConfig);
|
||||
|
||||
|
||||
expect(unified.service.serviceName).toBe('data-ingestion');
|
||||
expect(unified.redis).toBeDefined();
|
||||
expect(unified.redis?.host).toBe('localhost');
|
||||
|
|
@ -152,4 +152,4 @@ describe('UnifiedAppConfig', () => {
|
|||
expect(unified.postgres?.host).toBe('localhost');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,61 +1,63 @@
|
|||
import { z } from 'zod';
|
||||
import { environmentSchema } from './base.schema';
|
||||
import {
|
||||
postgresConfigSchema,
|
||||
mongodbConfigSchema,
|
||||
questdbConfigSchema,
|
||||
dragonflyConfigSchema
|
||||
} from './database.schema';
|
||||
import {
|
||||
serviceConfigSchema,
|
||||
loggingConfigSchema,
|
||||
queueConfigSchema,
|
||||
httpConfigSchema,
|
||||
webshareConfigSchema,
|
||||
browserConfigSchema,
|
||||
proxyConfigSchema
|
||||
} from './service.schema';
|
||||
|
||||
/**
|
||||
* Generic base application schema that can be extended by specific apps
|
||||
*/
|
||||
export const baseAppSchema = z.object({
|
||||
// Basic app info
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
environment: environmentSchema.default('development'),
|
||||
|
||||
// Service configuration
|
||||
service: serviceConfigSchema,
|
||||
|
||||
// Logging configuration
|
||||
log: loggingConfigSchema,
|
||||
|
||||
// Database configuration - apps can choose which databases they need
|
||||
database: z.object({
|
||||
postgres: postgresConfigSchema.optional(),
|
||||
mongodb: mongodbConfigSchema.optional(),
|
||||
questdb: questdbConfigSchema.optional(),
|
||||
dragonfly: dragonflyConfigSchema.optional(),
|
||||
}).optional(),
|
||||
|
||||
// Redis configuration (used for cache and queue)
|
||||
redis: dragonflyConfigSchema.optional(),
|
||||
|
||||
// Queue configuration
|
||||
queue: queueConfigSchema.optional(),
|
||||
|
||||
// HTTP client configuration
|
||||
http: httpConfigSchema.optional(),
|
||||
|
||||
// WebShare proxy configuration
|
||||
webshare: webshareConfigSchema.optional(),
|
||||
|
||||
// Browser configuration
|
||||
browser: browserConfigSchema.optional(),
|
||||
|
||||
// Proxy manager configuration
|
||||
proxy: proxyConfigSchema.optional(),
|
||||
});
|
||||
|
||||
export type BaseAppConfig = z.infer<typeof baseAppSchema>;
|
||||
import { z } from 'zod';
|
||||
import { environmentSchema } from './base.schema';
|
||||
import {
|
||||
dragonflyConfigSchema,
|
||||
mongodbConfigSchema,
|
||||
postgresConfigSchema,
|
||||
questdbConfigSchema,
|
||||
} from './database.schema';
|
||||
import {
|
||||
browserConfigSchema,
|
||||
httpConfigSchema,
|
||||
loggingConfigSchema,
|
||||
proxyConfigSchema,
|
||||
queueConfigSchema,
|
||||
serviceConfigSchema,
|
||||
webshareConfigSchema,
|
||||
} from './service.schema';
|
||||
|
||||
/**
|
||||
* Generic base application schema that can be extended by specific apps
|
||||
*/
|
||||
export const baseAppSchema = z.object({
|
||||
// Basic app info
|
||||
name: z.string(),
|
||||
version: z.string(),
|
||||
environment: environmentSchema.default('development'),
|
||||
|
||||
// Service configuration
|
||||
service: serviceConfigSchema,
|
||||
|
||||
// Logging configuration
|
||||
log: loggingConfigSchema,
|
||||
|
||||
// Database configuration - apps can choose which databases they need
|
||||
database: z
|
||||
.object({
|
||||
postgres: postgresConfigSchema.optional(),
|
||||
mongodb: mongodbConfigSchema.optional(),
|
||||
questdb: questdbConfigSchema.optional(),
|
||||
dragonfly: dragonflyConfigSchema.optional(),
|
||||
})
|
||||
.optional(),
|
||||
|
||||
// Redis configuration (used for cache and queue)
|
||||
redis: dragonflyConfigSchema.optional(),
|
||||
|
||||
// Queue configuration
|
||||
queue: queueConfigSchema.optional(),
|
||||
|
||||
// HTTP client configuration
|
||||
http: httpConfigSchema.optional(),
|
||||
|
||||
// WebShare proxy configuration
|
||||
webshare: webshareConfigSchema.optional(),
|
||||
|
||||
// Browser configuration
|
||||
browser: browserConfigSchema.optional(),
|
||||
|
||||
// Proxy manager configuration
|
||||
proxy: proxyConfigSchema.optional(),
|
||||
});
|
||||
|
||||
export type BaseAppConfig = z.infer<typeof baseAppSchema>;
|
||||
|
|
|
|||
|
|
@ -15,4 +15,3 @@ export type { BaseAppConfig } from './base-app.schema';
|
|||
// Export unified schema for standardized configuration
|
||||
export { unifiedAppSchema, toUnifiedConfig, getStandardServiceName } from './unified-app.schema';
|
||||
export type { UnifiedAppConfig } from './unified-app.schema';
|
||||
|
||||
|
|
|
|||
|
|
@ -100,8 +100,10 @@ export const proxyConfigSchema = z.object({
|
|||
enabled: z.boolean().default(false),
|
||||
cachePrefix: z.string().default('proxy:'),
|
||||
ttl: z.number().default(3600),
|
||||
webshare: z.object({
|
||||
apiKey: z.string(),
|
||||
apiUrl: z.string().default('https://proxy.webshare.io/api/v2/'),
|
||||
}).optional(),
|
||||
webshare: z
|
||||
.object({
|
||||
apiKey: z.string(),
|
||||
apiUrl: z.string().default('https://proxy.webshare.io/api/v2/'),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,62 +1,67 @@
|
|||
import { z } from 'zod';
|
||||
import { baseAppSchema } from './base-app.schema';
|
||||
import {
|
||||
postgresConfigSchema,
|
||||
mongodbConfigSchema,
|
||||
questdbConfigSchema,
|
||||
dragonflyConfigSchema
|
||||
import {
|
||||
dragonflyConfigSchema,
|
||||
mongodbConfigSchema,
|
||||
postgresConfigSchema,
|
||||
questdbConfigSchema,
|
||||
} from './database.schema';
|
||||
|
||||
/**
|
||||
* Unified application configuration schema that provides both nested and flat access
|
||||
* to database configurations for backward compatibility while maintaining a clean structure
|
||||
*/
|
||||
export const unifiedAppSchema = baseAppSchema.extend({
|
||||
// Flat database configs for DI system (these take precedence)
|
||||
redis: dragonflyConfigSchema.optional(),
|
||||
mongodb: mongodbConfigSchema.optional(),
|
||||
postgres: postgresConfigSchema.optional(),
|
||||
questdb: questdbConfigSchema.optional(),
|
||||
}).transform((data) => {
|
||||
// Ensure service.serviceName is set from service.name if not provided
|
||||
if (data.service && !data.service.serviceName) {
|
||||
data.service.serviceName = data.service.name.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
||||
}
|
||||
export const unifiedAppSchema = baseAppSchema
|
||||
.extend({
|
||||
// Flat database configs for DI system (these take precedence)
|
||||
redis: dragonflyConfigSchema.optional(),
|
||||
mongodb: mongodbConfigSchema.optional(),
|
||||
postgres: postgresConfigSchema.optional(),
|
||||
questdb: questdbConfigSchema.optional(),
|
||||
})
|
||||
.transform(data => {
|
||||
// Ensure service.serviceName is set from service.name if not provided
|
||||
if (data.service && !data.service.serviceName) {
|
||||
data.service.serviceName = data.service.name
|
||||
.replace(/([A-Z])/g, '-$1')
|
||||
.toLowerCase()
|
||||
.replace(/^-/, '');
|
||||
}
|
||||
|
||||
// If flat configs exist, ensure they're also in the nested database object
|
||||
if (data.redis || data.mongodb || data.postgres || data.questdb) {
|
||||
data.database = {
|
||||
...data.database,
|
||||
dragonfly: data.redis || data.database?.dragonfly,
|
||||
mongodb: data.mongodb || data.database?.mongodb,
|
||||
postgres: data.postgres || data.database?.postgres,
|
||||
questdb: data.questdb || data.database?.questdb,
|
||||
};
|
||||
}
|
||||
// If flat configs exist, ensure they're also in the nested database object
|
||||
if (data.redis || data.mongodb || data.postgres || data.questdb) {
|
||||
data.database = {
|
||||
...data.database,
|
||||
dragonfly: data.redis || data.database?.dragonfly,
|
||||
mongodb: data.mongodb || data.database?.mongodb,
|
||||
postgres: data.postgres || data.database?.postgres,
|
||||
questdb: data.questdb || data.database?.questdb,
|
||||
};
|
||||
}
|
||||
|
||||
// If nested configs exist but flat ones don't, copy them to flat structure
|
||||
if (data.database) {
|
||||
if (data.database.dragonfly && !data.redis) {
|
||||
data.redis = data.database.dragonfly;
|
||||
}
|
||||
if (data.database.mongodb && !data.mongodb) {
|
||||
data.mongodb = data.database.mongodb;
|
||||
}
|
||||
if (data.database.postgres && !data.postgres) {
|
||||
data.postgres = data.database.postgres;
|
||||
}
|
||||
if (data.database.questdb && !data.questdb) {
|
||||
// Handle the ilpPort -> influxPort mapping for DI system
|
||||
const questdbConfig = { ...data.database.questdb };
|
||||
if ('ilpPort' in questdbConfig && !('influxPort' in questdbConfig)) {
|
||||
(questdbConfig as any).influxPort = questdbConfig.ilpPort;
|
||||
// If nested configs exist but flat ones don't, copy them to flat structure
|
||||
if (data.database) {
|
||||
if (data.database.dragonfly && !data.redis) {
|
||||
data.redis = data.database.dragonfly;
|
||||
}
|
||||
if (data.database.mongodb && !data.mongodb) {
|
||||
data.mongodb = data.database.mongodb;
|
||||
}
|
||||
if (data.database.postgres && !data.postgres) {
|
||||
data.postgres = data.database.postgres;
|
||||
}
|
||||
if (data.database.questdb && !data.questdb) {
|
||||
// Handle the ilpPort -> influxPort mapping for DI system
|
||||
const questdbConfig = { ...data.database.questdb };
|
||||
if ('ilpPort' in questdbConfig && !('influxPort' in questdbConfig)) {
|
||||
(questdbConfig as any).influxPort = questdbConfig.ilpPort;
|
||||
}
|
||||
data.questdb = questdbConfig;
|
||||
}
|
||||
data.questdb = questdbConfig;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
return data;
|
||||
});
|
||||
|
||||
export type UnifiedAppConfig = z.infer<typeof unifiedAppSchema>;
|
||||
|
||||
|
|
@ -72,5 +77,8 @@ export function toUnifiedConfig(config: any): UnifiedAppConfig {
|
|||
*/
|
||||
export function getStandardServiceName(serviceName: string): string {
|
||||
// Convert camelCase to kebab-case
|
||||
return serviceName.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
||||
}
|
||||
return serviceName
|
||||
.replace(/([A-Z])/g, '-$1')
|
||||
.toLowerCase()
|
||||
.replace(/^-/, '');
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue