diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 20ed00966..333a0e439 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -15,7 +15,6 @@ "devDependencies": { "@kit/eslint-config": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/node": "^22.13.0" }, diff --git a/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-factory.service.ts b/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-factory.service.ts index f77bf9424..6010ce76c 100644 --- a/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-factory.service.ts +++ b/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-factory.service.ts @@ -3,37 +3,44 @@ import 'server-only'; import { z } from 'zod'; import { - BillingConfig, + type BillingConfig, type BillingProviderSchema, BillingWebhookHandlerService, } from '@kit/billing'; +import { createRegistry } from '@kit/shared/registry'; -export class BillingEventHandlerFactoryService { - static async GetProviderStrategy( - provider: z.infer, - config: BillingConfig, - ): Promise { - switch (provider) { - case 'stripe': { - const { StripeWebhookHandlerService } = await import('@kit/stripe'); +/** + * @description Creates a registry for billing webhook handlers + * @param config - The billing config + * @returns The billing webhook handler registry + */ +export function createBillingEventHandlerFactoryService(config: BillingConfig) { + // Create a registry for billing webhook handlers + const billingWebhookHandlerRegistry = createRegistry< + BillingWebhookHandlerService, + z.infer + >(); - return new StripeWebhookHandlerService(config); - } + // Register the Stripe webhook handler + billingWebhookHandlerRegistry.register('stripe', async () => { + const { StripeWebhookHandlerService } = await import('@kit/stripe'); - case 'lemon-squeezy': { - const { LemonSqueezyWebhookHandlerService } = await import( - '@kit/lemon-squeezy' - ); + return new StripeWebhookHandlerService(config); + }); - return new LemonSqueezyWebhookHandlerService(config); - } + // Register the Lemon Squeezy webhook handler + billingWebhookHandlerRegistry.register('lemon-squeezy', async () => { + const { LemonSqueezyWebhookHandlerService } = await import( + '@kit/lemon-squeezy' + ); - case 'paddle': { - throw new Error('Paddle is not supported yet'); - } + return new LemonSqueezyWebhookHandlerService(config); + }); - default: - throw new Error(`Unsupported billing provider: ${provider as string}`); - } - } + // Register Paddle webhook handler (not implemented yet) + billingWebhookHandlerRegistry.register('paddle', () => { + throw new Error('Paddle is not supported yet'); + }); + + return billingWebhookHandlerRegistry; } diff --git a/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-provider.ts b/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-provider.ts index 5e72c9973..43c2d9d83 100644 --- a/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-provider.ts +++ b/packages/billing/gateway/src/server/services/billing-event-handler/billing-event-handler-provider.ts @@ -5,7 +5,7 @@ import { SupabaseClient } from '@supabase/supabase-js'; import { BillingConfig } from '@kit/billing'; import { Database, Enums } from '@kit/supabase/database'; -import { BillingEventHandlerFactoryService } from './billing-event-handler-factory.service'; +import { createBillingEventHandlerFactoryService } from './billing-event-handler-factory.service'; import { createBillingEventHandlerService } from './billing-event-handler.service'; // a function that returns a Supabase client @@ -25,10 +25,8 @@ export async function getBillingEventHandlerService( provider: BillingProvider, config: BillingConfig, ) { - const strategy = await BillingEventHandlerFactoryService.GetProviderStrategy( - provider, - config, - ); + const strategy = + await createBillingEventHandlerFactoryService(config).get(provider); return createBillingEventHandlerService(clientProvider, strategy); } diff --git a/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-factory.service.ts b/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-factory.service.ts deleted file mode 100644 index faa2d7d92..000000000 --- a/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-factory.service.ts +++ /dev/null @@ -1,37 +0,0 @@ -import 'server-only'; - -import { z } from 'zod'; - -import { - type BillingProviderSchema, - BillingStrategyProviderService, -} from '@kit/billing'; - -export class BillingGatewayFactoryService { - static async GetProviderStrategy( - provider: z.infer, - ): Promise { - switch (provider) { - case 'stripe': { - const { StripeBillingStrategyService } = await import('@kit/stripe'); - - return new StripeBillingStrategyService(); - } - - case 'lemon-squeezy': { - const { LemonSqueezyBillingStrategyService } = await import( - '@kit/lemon-squeezy' - ); - - return new LemonSqueezyBillingStrategyService(); - } - - case 'paddle': { - throw new Error('Paddle is not supported yet'); - } - - default: - throw new Error(`Unsupported billing provider: ${provider as string}`); - } - } -} diff --git a/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-registry.ts b/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-registry.ts new file mode 100644 index 000000000..e04be9ff0 --- /dev/null +++ b/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway-registry.ts @@ -0,0 +1,34 @@ +import 'server-only'; + +import { z } from 'zod'; + +import { + type BillingProviderSchema, + BillingStrategyProviderService, +} from '@kit/billing'; +import { createRegistry } from '@kit/shared/registry'; + +// Create a registry for billing strategy providers +export const billingStrategyRegistry = createRegistry< + BillingStrategyProviderService, + z.infer +>(); + +// Register the Stripe billing strategy +billingStrategyRegistry.register('stripe', async () => { + const { StripeBillingStrategyService } = await import('@kit/stripe'); + return new StripeBillingStrategyService(); +}); + +// Register the Lemon Squeezy billing strategy +billingStrategyRegistry.register('lemon-squeezy', async () => { + const { LemonSqueezyBillingStrategyService } = await import( + '@kit/lemon-squeezy' + ); + return new LemonSqueezyBillingStrategyService(); +}); + +// Register Paddle billing strategy (not implemented yet) +billingStrategyRegistry.register('paddle', () => { + throw new Error('Paddle is not supported yet'); +}); diff --git a/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway.service.ts b/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway.service.ts index 9e619ec91..551153106 100644 --- a/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway.service.ts +++ b/packages/billing/gateway/src/server/services/billing-gateway/billing-gateway.service.ts @@ -11,7 +11,7 @@ import { UpdateSubscriptionParamsSchema, } from '@kit/billing/schema'; -import { BillingGatewayFactoryService } from './billing-gateway-factory.service'; +import { billingStrategyRegistry } from './billing-gateway-registry'; export function createBillingGatewayService( provider: z.infer, @@ -138,6 +138,6 @@ class BillingGatewayService { } private getStrategy() { - return BillingGatewayFactoryService.GetProviderStrategy(this.provider); + return billingStrategyRegistry.get(this.provider); } } diff --git a/packages/cms/core/package.json b/packages/cms/core/package.json index af4e1d6cd..fc188d24a 100644 --- a/packages/cms/core/package.json +++ b/packages/cms/core/package.json @@ -17,6 +17,7 @@ "@kit/eslint-config": "workspace:*", "@kit/keystatic": "workspace:*", "@kit/prettier-config": "workspace:*", + "@kit/shared": "workspace:*", "@kit/tsconfig": "workspace:*", "@kit/wordpress": "workspace:*", "@types/node": "^22.13.0" @@ -35,4 +36,4 @@ ] } } -} \ No newline at end of file +} diff --git a/packages/cms/core/src/create-cms-client.ts b/packages/cms/core/src/create-cms-client.ts index 0f824903c..38226bc39 100644 --- a/packages/cms/core/src/create-cms-client.ts +++ b/packages/cms/core/src/create-cms-client.ts @@ -1,10 +1,26 @@ import { CmsClient, CmsType } from '@kit/cms-types'; +import { createRegistry } from '@kit/shared/registry'; /** * The type of CMS client to use. */ const CMS_CLIENT = process.env.CMS_CLIENT as CmsType; +// Create a registry for CMS client implementations +const cmsRegistry = createRegistry(); + +// Register the WordPress CMS client implementation +cmsRegistry.register('wordpress', async () => { + const { createWordpressClient } = await import('@kit/wordpress'); + return createWordpressClient(); +}); + +// Register the Keystatic CMS client implementation +cmsRegistry.register('keystatic', async () => { + const { createKeystaticClient } = await import('@kit/keystatic'); + return createKeystaticClient(); +}); + /** * Creates a CMS client based on the specified type. * @@ -12,33 +28,6 @@ const CMS_CLIENT = process.env.CMS_CLIENT as CmsType; * @returns {Promise} A Promise that resolves to the created CMS client. * @throws {Error} If the specified CMS type is unknown. */ -export async function createCmsClient( - type: CmsType = CMS_CLIENT, -): Promise { - return cmsClientFactory(type); -} - -/** - * Creates a CMS client based on the specified type. - * - * @param {CmsType} type - The type of CMS client to create. - * @returns {Promise} A Promise that resolves to the created CMS client. - */ -async function cmsClientFactory(type: CmsType): Promise { - switch (type) { - case 'wordpress': { - const { createWordpressClient } = await import('@kit/wordpress'); - - return createWordpressClient(); - } - - case 'keystatic': { - const { createKeystaticClient } = await import('@kit/keystatic'); - - return createKeystaticClient(); - } - - default: - throw new Error(`Unknown CMS type`); - } +export async function createCmsClient(type: CmsType = CMS_CLIENT) { + return cmsRegistry.get(type); } diff --git a/packages/database-webhooks/package.json b/packages/database-webhooks/package.json index 2bc6858a7..667a580df 100644 --- a/packages/database-webhooks/package.json +++ b/packages/database-webhooks/package.json @@ -20,7 +20,6 @@ "@kit/shared": "workspace:*", "@kit/stripe": "workspace:*", "@kit/supabase": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/team-accounts": "workspace:*", "@kit/tsconfig": "workspace:*", "@supabase/supabase-js": "2.48.1", diff --git a/packages/i18n/package.json b/packages/i18n/package.json index 33bd6f030..de4f404ac 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -19,7 +19,6 @@ "@kit/eslint-config": "workspace:*", "@kit/prettier-config": "workspace:*", "@kit/shared": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@tanstack/react-query": "5.66.0", "next": "15.1.6", diff --git a/packages/mailers/core/package.json b/packages/mailers/core/package.json index ca864ac47..a94c89fa8 100644 --- a/packages/mailers/core/package.json +++ b/packages/mailers/core/package.json @@ -14,10 +14,11 @@ }, "devDependencies": { "@kit/eslint-config": "workspace:*", + "@kit/mailers-shared": "workspace:*", "@kit/nodemailer": "workspace:*", "@kit/prettier-config": "workspace:*", "@kit/resend": "workspace:*", - "@kit/tailwind-config": "workspace:*", + "@kit/shared": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/node": "^22.13.0", "zod": "^3.24.1" diff --git a/packages/mailers/core/src/index.ts b/packages/mailers/core/src/index.ts index 134e54ecb..7bc970381 100644 --- a/packages/mailers/core/src/index.ts +++ b/packages/mailers/core/src/index.ts @@ -1,37 +1,12 @@ -import { z } from 'zod'; - -const MAILER_PROVIDER = z - .enum(['nodemailer', 'resend']) - .default('nodemailer') - .parse(process.env.MAILER_PROVIDER); +import { MAILER_PROVIDER } from './provider-enum'; +import { mailerRegistry } from './registry'; /** - * @description Get the mailer based on the environment variable. + * @name getMailer + * @description Get the mailer based on the environment variable using the registry internally. */ -export async function getMailer() { - switch (MAILER_PROVIDER) { - case 'nodemailer': - return getNodemailer(); - - case 'resend': { - const { createResendMailer } = await import('@kit/resend'); - - return createResendMailer(); - } - - default: - throw new Error(`Invalid mailer: ${MAILER_PROVIDER as string}`); - } +export function getMailer() { + return mailerRegistry.get(MAILER_PROVIDER); } -async function getNodemailer() { - if (process.env.NEXT_RUNTIME === 'nodejs') { - const { createNodemailerService } = await import('@kit/nodemailer'); - - return createNodemailerService(); - } else { - throw new Error( - 'Nodemailer is not available on the edge runtime. Please use another mailer.', - ); - } -} +export { MAILER_PROVIDER }; diff --git a/packages/mailers/core/src/provider-enum.ts b/packages/mailers/core/src/provider-enum.ts new file mode 100644 index 000000000..f003de3ee --- /dev/null +++ b/packages/mailers/core/src/provider-enum.ts @@ -0,0 +1,16 @@ +import { z } from 'zod'; + +const MAILER_PROVIDERS = [ + 'nodemailer', + 'resend', + // add more providers here +] as const; + +const MAILER_PROVIDER = z + .enum(MAILER_PROVIDERS) + .default('nodemailer') + .parse(process.env.MAILER_PROVIDER); + +export { MAILER_PROVIDER }; + +export type MailerProvider = (typeof MAILER_PROVIDERS)[number]; diff --git a/packages/mailers/core/src/registry.ts b/packages/mailers/core/src/registry.ts new file mode 100644 index 000000000..392217541 --- /dev/null +++ b/packages/mailers/core/src/registry.ts @@ -0,0 +1,26 @@ +import { Mailer } from '@kit/mailers-shared'; +import { createRegistry } from '@kit/shared/registry'; + +import { MailerProvider } from './provider-enum'; + +const mailerRegistry = createRegistry(); + +mailerRegistry.register('nodemailer', async () => { + if (process.env.NEXT_RUNTIME === 'nodejs') { + const { createNodemailerService } = await import('@kit/nodemailer'); + + return createNodemailerService(); + } else { + throw new Error( + 'Nodemailer is not available on the edge runtime. Please use another mailer.', + ); + } +}); + +mailerRegistry.register('resend', async () => { + const { createResendMailer } = await import('@kit/resend'); + + return createResendMailer(); +}); + +export { mailerRegistry }; diff --git a/packages/mailers/nodemailer/package.json b/packages/mailers/nodemailer/package.json index d5bf655bb..fca5356f1 100644 --- a/packages/mailers/nodemailer/package.json +++ b/packages/mailers/nodemailer/package.json @@ -19,7 +19,6 @@ "@kit/eslint-config": "workspace:*", "@kit/mailers-shared": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/nodemailer": "6.4.17", "zod": "^3.24.1" diff --git a/packages/mailers/resend/package.json b/packages/mailers/resend/package.json index 7f51495f9..04c828324 100644 --- a/packages/mailers/resend/package.json +++ b/packages/mailers/resend/package.json @@ -16,7 +16,6 @@ "@kit/eslint-config": "workspace:*", "@kit/mailers-shared": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/node": "^22.13.0", "zod": "^3.24.1" diff --git a/packages/mailers/shared/package.json b/packages/mailers/shared/package.json index 8e1f307e7..bba45e4d7 100644 --- a/packages/mailers/shared/package.json +++ b/packages/mailers/shared/package.json @@ -15,7 +15,6 @@ "devDependencies": { "@kit/eslint-config": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "zod": "^3.24.1" }, diff --git a/packages/monitoring/api/package.json b/packages/monitoring/api/package.json index da8e625cb..ca84bac43 100644 --- a/packages/monitoring/api/package.json +++ b/packages/monitoring/api/package.json @@ -22,10 +22,11 @@ "@kit/monitoring-core": "workspace:*", "@kit/prettier-config": "workspace:*", "@kit/sentry": "workspace:*", - "@kit/tailwind-config": "workspace:*", + "@kit/shared": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/react": "19.0.8", - "react": "19.0.0" + "react": "19.0.0", + "zod": "^3.24.1" }, "eslintConfig": { "root": true, diff --git a/packages/monitoring/api/src/components/provider.tsx b/packages/monitoring/api/src/components/provider.tsx index 266345cf6..8a585f49b 100644 --- a/packages/monitoring/api/src/components/provider.tsx +++ b/packages/monitoring/api/src/components/provider.tsx @@ -2,38 +2,64 @@ import { lazy } from 'react'; -import { getMonitoringProvider } from '../get-monitoring-provider'; -import { InstrumentationProvider } from '../monitoring-providers.enum'; +import { createRegistry } from '@kit/shared/registry'; -const BaselimeProvider = lazy(async () => { +import { + MonitoringProvider as MonitoringProviderType, + getMonitoringProvider, +} from '../get-monitoring-provider'; + +// Define the type for our provider components +type ProviderComponent = { + default: React.ComponentType; +}; + +// Create a registry for monitoring providers +const monitoringProviderRegistry = createRegistry< + ProviderComponent, + NonNullable +>(); + +// Register the Baselime provider +monitoringProviderRegistry.register('baselime', async () => { const { BaselimeProvider } = await import('@kit/baselime/provider'); return { - default: BaselimeProvider, + default: function BaselimeProviderWrapper({ + children, + }: React.PropsWithChildren) { + return {children}; + }, }; }); -const SentryProvider = lazy(async () => { +// Register the Sentry provider +monitoringProviderRegistry.register('sentry', async () => { const { SentryProvider } = await import('@kit/sentry/provider'); return { - default: SentryProvider, + default: function SentryProviderWrapper({ + children, + }: React.PropsWithChildren) { + return {children}; + }, }; }); +/** + * @name MonitoringProvider + * @description This component is used to wrap the application with the appropriate monitoring provider. + * @param props + * @returns + */ export function MonitoringProvider(props: React.PropsWithChildren) { const provider = getMonitoringProvider(); - switch (provider) { - case InstrumentationProvider.Baselime: - return ( - {props.children} - ); - - case InstrumentationProvider.Sentry: - return {props.children}; - - default: - return <>{props.children}; + if (!provider) { + return <>{props.children}; } + + const Provider = lazy(() => monitoringProviderRegistry.get(provider)); + + return {props.children}; } diff --git a/packages/monitoring/api/src/get-monitoring-provider.ts b/packages/monitoring/api/src/get-monitoring-provider.ts index ff7771790..6ee0812e9 100644 --- a/packages/monitoring/api/src/get-monitoring-provider.ts +++ b/packages/monitoring/api/src/get-monitoring-provider.ts @@ -1,7 +1,9 @@ -import { InstrumentationProvider } from './monitoring-providers.enum'; +import { z } from 'zod'; + +export const MONITORING_PROVIDER = z.enum(['baselime', 'sentry']).optional(); + +export type MonitoringProvider = z.infer; export function getMonitoringProvider() { - return process.env.NEXT_PUBLIC_MONITORING_PROVIDER as - | InstrumentationProvider - | undefined; + return MONITORING_PROVIDER.parse(process.env.NEXT_PUBLIC_MONITORING_PROVIDER); } diff --git a/packages/monitoring/api/src/instrumentation.ts b/packages/monitoring/api/src/instrumentation.ts index 787c439c0..13eb8bdf4 100644 --- a/packages/monitoring/api/src/instrumentation.ts +++ b/packages/monitoring/api/src/instrumentation.ts @@ -1,37 +1,51 @@ -import { getMonitoringProvider } from './get-monitoring-provider'; -import { InstrumentationProvider } from './monitoring-providers.enum'; +import { createRegistry } from '@kit/shared/registry'; -const PROVIDER = getMonitoringProvider(); +import { + MonitoringProvider, + getMonitoringProvider, +} from './get-monitoring-provider'; + +// Define a type for the instrumentation registration implementation +type InstrumentationRegistration = { + register: () => Promise | void; +}; + +// Create a registry for instrumentation providers, using literal strings 'baselime' and 'sentry' +const instrumentationRegistry = createRegistry< + InstrumentationRegistration, + NonNullable +>(); + +// Register the 'baselime' instrumentation provider +instrumentationRegistry.register('baselime', async () => { + const { registerInstrumentation } = await import( + '@kit/baselime/instrumentation' + ); + + return { register: registerInstrumentation }; +}); + +// Register the 'sentry' instrumentation provider with a no-op registration, since Sentry v8 sets up automatically +instrumentationRegistry.register('sentry', () => { + return { + register: () => { + return; + }, + }; +}); /** * @name registerMonitoringInstrumentation - * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable. - * - * Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider. + * @description Register monitoring instrumentation based on the MONITORING_PROVIDER environment variable using the registry internally. */ export async function registerMonitoringInstrumentation() { - if (!PROVIDER) { + const provider = getMonitoringProvider(); + + if (!provider) { return; } - switch (PROVIDER) { - case InstrumentationProvider.Baselime: { - const { registerInstrumentation } = await import( - '@kit/baselime/instrumentation' - ); + const instrumentation = await instrumentationRegistry.get(provider); - return registerInstrumentation(); - } - - case InstrumentationProvider.Sentry: { - // Sentry v8 automatically sets this up - - return; - } - - default: - throw new Error( - `Unknown instrumentation provider: ${process.env.NEXT_PUBLIC_MONITORING_PROVIDER}`, - ); - } + return instrumentation.register(); } diff --git a/packages/monitoring/api/src/monitoring-providers.enum.ts b/packages/monitoring/api/src/monitoring-providers.enum.ts deleted file mode 100644 index 030610031..000000000 --- a/packages/monitoring/api/src/monitoring-providers.enum.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum InstrumentationProvider { - Baselime = 'baselime', - Sentry = 'sentry', -} diff --git a/packages/monitoring/api/src/services/get-server-monitoring-service.ts b/packages/monitoring/api/src/services/get-server-monitoring-service.ts index 236e6d77c..23f3a61fb 100644 --- a/packages/monitoring/api/src/services/get-server-monitoring-service.ts +++ b/packages/monitoring/api/src/services/get-server-monitoring-service.ts @@ -1,42 +1,52 @@ -import { ConsoleMonitoringService } from '@kit/monitoring-core'; +import { + ConsoleMonitoringService, + MonitoringService, +} from '@kit/monitoring-core'; +import { createRegistry } from '@kit/shared/registry'; -import { getMonitoringProvider } from '../get-monitoring-provider'; -import { InstrumentationProvider } from '../monitoring-providers.enum'; +import { + MonitoringProvider, + getMonitoringProvider, +} from '../get-monitoring-provider'; -const MONITORING_PROVIDER = getMonitoringProvider(); +// create a registry for the server monitoring services +const serverMonitoringRegistry = createRegistry< + MonitoringService, + NonNullable +>(); + +// Register the 'baselime' monitoring service +serverMonitoringRegistry.register('baselime', async () => { + const { BaselimeServerMonitoringService } = await import( + '@kit/baselime/server' + ); + + return new BaselimeServerMonitoringService(); +}); + +// Register the 'sentry' monitoring service +serverMonitoringRegistry.register('sentry', async () => { + const { SentryMonitoringService } = await import('@kit/sentry'); + + return new SentryMonitoringService(); +}); + +// if you have a new monitoring provider, you can register it here +// /** * @name getServerMonitoringService * @description Get the monitoring service based on the MONITORING_PROVIDER environment variable. */ export async function getServerMonitoringService() { - if (!MONITORING_PROVIDER) { + const provider = getMonitoringProvider(); + + if (!provider) { console.info( `No instrumentation provider specified. Returning console service...`, ); - return new ConsoleMonitoringService(); } - switch (MONITORING_PROVIDER) { - case InstrumentationProvider.Baselime: { - const { BaselimeServerMonitoringService } = await import( - '@kit/baselime/server' - ); - - return new BaselimeServerMonitoringService(); - } - - case InstrumentationProvider.Sentry: { - const { SentryMonitoringService } = await import('@kit/sentry'); - - return new SentryMonitoringService(); - } - - default: { - throw new Error( - `Please set the MONITORING_PROVIDER environment variable to register the monitoring instrumentation provider.`, - ); - } - } + return serverMonitoringRegistry.get(provider); } diff --git a/packages/monitoring/baselime/package.json b/packages/monitoring/baselime/package.json index b48a806d3..a6c488893 100644 --- a/packages/monitoring/baselime/package.json +++ b/packages/monitoring/baselime/package.json @@ -23,7 +23,6 @@ "devDependencies": { "@kit/eslint-config": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/react": "19.0.8", "react": "19.0.0", diff --git a/packages/monitoring/core/package.json b/packages/monitoring/core/package.json index a68553c7d..9df67d8a6 100644 --- a/packages/monitoring/core/package.json +++ b/packages/monitoring/core/package.json @@ -16,7 +16,6 @@ "devDependencies": { "@kit/eslint-config": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/react": "19.0.8", "react": "19.0.0" diff --git a/packages/monitoring/sentry/package.json b/packages/monitoring/sentry/package.json index c2fe70b6a..7e779ae16 100644 --- a/packages/monitoring/sentry/package.json +++ b/packages/monitoring/sentry/package.json @@ -22,7 +22,6 @@ "@kit/eslint-config": "workspace:*", "@kit/monitoring-core": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/react": "19.0.8", "react": "19.0.0" diff --git a/packages/next/package.json b/packages/next/package.json index 1034dc938..a71c80acc 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -19,7 +19,6 @@ "@kit/monitoring": "workspace:*", "@kit/prettier-config": "workspace:*", "@kit/supabase": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@supabase/supabase-js": "2.48.1", "next": "15.1.6", diff --git a/packages/shared/package.json b/packages/shared/package.json index 2ffbe5474..2708ba59f 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -13,12 +13,12 @@ "./logger": "./src/logger/index.ts", "./utils": "./src/utils.ts", "./hooks": "./src/hooks/index.ts", - "./events": "./src/events/index.tsx" + "./events": "./src/events/index.tsx", + "./registry": "./src/registry/index.ts" }, "devDependencies": { "@kit/eslint-config": "workspace:*", "@kit/prettier-config": "workspace:*", - "@kit/tailwind-config": "workspace:*", "@kit/tsconfig": "workspace:*", "@types/react": "19.0.8" }, diff --git a/packages/shared/src/logger/index.ts b/packages/shared/src/logger/index.ts index c21d244dc..ceec9e747 100644 --- a/packages/shared/src/logger/index.ts +++ b/packages/shared/src/logger/index.ts @@ -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(); + +// 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 { - 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 }; diff --git a/packages/shared/src/registry/index.ts b/packages/shared/src/registry/index.ts new file mode 100644 index 000000000..55cc2ca10 --- /dev/null +++ b/packages/shared/src/registry/index.ts @@ -0,0 +1,108 @@ +/** + * Implementation factory type + */ +export type ImplementationFactory = () => + | T + | Promise; + +/** + * Public API types with improved get method + */ +export interface Registry { + register: ( + name: Names, + factory: ImplementationFactory, + ) => Registry; + + // Overloaded get method that infers return types based on input. + get: { + (name: K): Promise; + ( + ...names: K + ): Promise<{ [P in keyof K]: T }>; + }; + + setup: (group?: string) => Promise; + addSetup: ( + group: string, + callback: () => Promise, + ) => Registry; +} + +/** + * @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 { + const implementations = new Map>(); + const setupCallbacks = new Map Promise>>(); + const setupPromises = new Map>(); + + const registry: Registry = { + 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['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; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c51b4c404..0c44e3017 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -237,9 +237,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript @@ -443,6 +440,9 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier + '@kit/shared': + specifier: workspace:* + version: link:../../shared '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -555,9 +555,6 @@ importers: '@kit/supabase': specifier: workspace:* version: link:../supabase - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../tooling/tailwind '@kit/team-accounts': specifier: workspace:* version: link:../features/team-accounts @@ -964,9 +961,6 @@ importers: '@kit/shared': specifier: workspace:* version: link:../shared - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript @@ -991,6 +985,9 @@ importers: '@kit/eslint-config': specifier: workspace:* version: link:../../../tooling/eslint + '@kit/mailers-shared': + specifier: workspace:* + version: link:../shared '@kit/nodemailer': specifier: workspace:* version: link:../nodemailer @@ -1000,9 +997,9 @@ importers: '@kit/resend': specifier: workspace:* version: link:../resend - '@kit/tailwind-config': + '@kit/shared': specifier: workspace:* - version: link:../../../tooling/tailwind + version: link:../../shared '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1028,9 +1025,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1052,9 +1046,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1073,9 +1064,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1100,9 +1088,9 @@ importers: '@kit/sentry': specifier: workspace:* version: link:../sentry - '@kit/tailwind-config': + '@kit/shared': specifier: workspace:* - version: link:../../../tooling/tailwind + version: link:../../shared '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1112,6 +1100,9 @@ importers: react: specifier: 19.0.0 version: 19.0.0 + zod: + specifier: ^3.24.1 + version: 3.24.1 packages/monitoring/baselime: dependencies: @@ -1131,9 +1122,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1155,9 +1143,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1183,9 +1168,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../../tooling/typescript @@ -1213,9 +1195,6 @@ importers: '@kit/supabase': specifier: workspace:* version: link:../supabase - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript @@ -1241,9 +1220,6 @@ importers: '@kit/prettier-config': specifier: workspace:* version: link:../../tooling/prettier - '@kit/tailwind-config': - specifier: workspace:* - version: link:../../tooling/tailwind '@kit/tsconfig': specifier: workspace:* version: link:../../tooling/typescript @@ -4122,9 +4098,6 @@ packages: '@types/eslint@8.56.12': resolution: {integrity: sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==} - '@types/eslint@9.6.1': - resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} - '@types/estree-jsx@1.0.5': resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} @@ -11437,7 +11410,7 @@ snapshots: '@types/eslint-scope@3.7.7': dependencies: - '@types/eslint': 9.6.1 + '@types/eslint': 8.56.12 '@types/estree': 1.0.6 '@types/eslint@8.56.12': @@ -11445,11 +11418,6 @@ snapshots: '@types/estree': 1.0.6 '@types/json-schema': 7.0.15 - '@types/eslint@9.6.1': - dependencies: - '@types/estree': 1.0.6 - '@types/json-schema': 7.0.15 - '@types/estree-jsx@1.0.5': dependencies: '@types/estree': 1.0.6