Registry API Refactoring (#144)
* Refactor core to use a flexible registry pattern - Introduce a new registry mechanism for mailer providers - Extract mailer provider enum to a separate file - Implement dynamic mailer loading using a registry - Update package dependencies and exports - Improve modularity and extensibility of mailer implementation * Refactor monitoring and billing services to use a flexible registry pattern - Introduce a shared registry mechanism for dynamic service loading - Replace static switch-based implementations with a registry-based approach - Update instrumentation, CMS, and monitoring services to use the new registry - Improve modularity and extensibility of service implementations - Add Zod-based type-safe provider validation * Simplify async registration in monitoring and billing services - Remove unnecessary async wrappers for no-op registrations - Update type definitions to support both async and sync registration functions - Standardize registration approach for Paddle and Sentry providers * Remove Tailwind package from packages where it is not being needed * Remove Tailwind config references from pnpm-lock.yaml * Update instrumentation registry to support dynamic monitoring providers - Modify type definition to use NonNullable MonitoringProvider - Import MonitoringProvider type from get-monitoring-provider - Enhance type safety for instrumentation registration
This commit is contained in:
committed by
GitHub
parent
3140f0cf21
commit
4a47df81db
@@ -1,23 +1,25 @@
|
||||
import { createRegistry } from '../registry';
|
||||
import { Logger as LoggerInstance } from './logger';
|
||||
|
||||
const LOGGER = process.env.LOGGER ?? 'pino';
|
||||
// Define the type for the logger provider. Currently supporting 'pino'.
|
||||
type LoggerProvider = 'pino';
|
||||
|
||||
/*
|
||||
* Logger
|
||||
* By default, the logger is set to use Pino. To change the logger, update the import statement below.
|
||||
* to your desired logger implementation.
|
||||
const LOGGER = (process.env.LOGGER ?? 'pino') as LoggerProvider;
|
||||
|
||||
// Create a registry for logger implementations
|
||||
const loggerRegistry = createRegistry<LoggerInstance, LoggerProvider>();
|
||||
|
||||
// Register the 'pino' logger implementation
|
||||
loggerRegistry.register('pino', async () => {
|
||||
const { Logger: PinoLogger } = await import('./impl/pino');
|
||||
|
||||
return PinoLogger;
|
||||
});
|
||||
|
||||
/**
|
||||
* @name getLogger
|
||||
* @description Retrieves the logger implementation based on the LOGGER environment variable using the registry API.
|
||||
*/
|
||||
async function getLogger(): Promise<LoggerInstance> {
|
||||
switch (LOGGER) {
|
||||
case 'pino': {
|
||||
const { Logger: PinoLogger } = await import('./impl/pino');
|
||||
|
||||
return PinoLogger;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown logger: ${process.env.LOGGER}`);
|
||||
}
|
||||
export async function getLogger() {
|
||||
return loggerRegistry.get(LOGGER);
|
||||
}
|
||||
|
||||
export { getLogger };
|
||||
|
||||
108
packages/shared/src/registry/index.ts
Normal file
108
packages/shared/src/registry/index.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Implementation factory type
|
||||
*/
|
||||
export type ImplementationFactory<T> = () =>
|
||||
| T
|
||||
| Promise<T>;
|
||||
|
||||
/**
|
||||
* Public API types with improved get method
|
||||
*/
|
||||
export interface Registry<T, Names extends string> {
|
||||
register: (
|
||||
name: Names,
|
||||
factory: ImplementationFactory<T>,
|
||||
) => Registry<T, Names>;
|
||||
|
||||
// Overloaded get method that infers return types based on input.
|
||||
get: {
|
||||
<K extends Names>(name: K): Promise<T>;
|
||||
<K extends [Names, ...Names[]]>(
|
||||
...names: K
|
||||
): Promise<{ [P in keyof K]: T }>;
|
||||
};
|
||||
|
||||
setup: (group?: string) => Promise<void>;
|
||||
addSetup: (
|
||||
group: string,
|
||||
callback: () => Promise<void>,
|
||||
) => Registry<T, Names>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name createRegistry
|
||||
* @description Creates a new registry instance with the provided implementations.
|
||||
* @returns A new registry instance.
|
||||
*/
|
||||
export function createRegistry<
|
||||
T,
|
||||
Names extends string = string,
|
||||
>(): Registry<T, Names> {
|
||||
const implementations = new Map<Names, ImplementationFactory<T>>();
|
||||
const setupCallbacks = new Map<string, Array<() => Promise<void>>>();
|
||||
const setupPromises = new Map<string, Promise<void>>();
|
||||
|
||||
const registry: Registry<T, Names> = {
|
||||
register(name, factory) {
|
||||
implementations.set(name, factory);
|
||||
return registry;
|
||||
},
|
||||
|
||||
// Updated get method overload that supports tuple inference
|
||||
get: (async (...names: Names[]) => {
|
||||
await registry.setup();
|
||||
|
||||
if (names.length === 1) {
|
||||
return await getImplementation(names[0]!);
|
||||
}
|
||||
|
||||
return await Promise.all(names.map((name) => getImplementation(name)));
|
||||
}) as Registry<T, Names>['get'],
|
||||
|
||||
async setup(group?: string) {
|
||||
if (group) {
|
||||
if (!setupPromises.has(group)) {
|
||||
const callbacks = setupCallbacks.get(group) ?? [];
|
||||
|
||||
setupPromises.set(
|
||||
group,
|
||||
Promise.all(callbacks.map((cb) => cb())).then(() => void 0),
|
||||
);
|
||||
}
|
||||
|
||||
return setupPromises.get(group);
|
||||
}
|
||||
|
||||
const groups = Array.from(setupCallbacks.keys());
|
||||
|
||||
await Promise.all(groups.map((group) => registry.setup(group)));
|
||||
},
|
||||
|
||||
addSetup(group, callback) {
|
||||
if (!setupCallbacks.has(group)) {
|
||||
setupCallbacks.set(group, []);
|
||||
}
|
||||
|
||||
setupCallbacks.get(group)!.push(callback);
|
||||
return registry;
|
||||
},
|
||||
};
|
||||
|
||||
async function getImplementation(name: Names) {
|
||||
const factory = implementations.get(name);
|
||||
|
||||
if (!factory) {
|
||||
throw new Error(`Implementation "${name}" not found`);
|
||||
}
|
||||
|
||||
const implementation = await factory();
|
||||
|
||||
if (!implementation) {
|
||||
throw new Error(`Implementation "${name}" is not available`);
|
||||
}
|
||||
|
||||
return implementation;
|
||||
}
|
||||
|
||||
return registry;
|
||||
}
|
||||
Reference in New Issue
Block a user